/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2025 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./mdevices.h" #include "aqhome-cgi/service/module.h" #include "aqhome-cgi/modules/mdataclient.h" #include #include #include /* ------------------------------------------------------------------------------------------------ * defs and enums * ------------------------------------------------------------------------------------------------ */ #define GBAS GWEN_Buffer_AppendString #define GBAA GWEN_Buffer_AppendArgs #define P_DEVICEREAD AQH_MODDEVICES_PERMS_DEVICEREAD #define P_VALUEREAD AQH_MODDEVICES_PERMS_VALUEREAD #define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static void _createPermDefList(AQH_MODULE *m); static void _createRoleList(AQH_MODULE *m); static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName); static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem); static void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqValuesGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _runIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); static void _runValues(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); static void _runSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); static void _writeValueListToTable(AQH_DATACLIENT *dc, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf); static void _writeValueToTable(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf); static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf); static uint32_t _colorFromHexString(const char *s); static void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue); static void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue); static void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue); static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf); /* ------------------------------------------------------------------------------------------------ * vars * ------------------------------------------------------------------------------------------------ */ static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={ {"index.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD, _handleRqIndexGet}, {"values.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesGet}, {"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost}, {NULL, 0, 0, NULL} }; /* ------------------------------------------------------------------------------------------------ * code * ------------------------------------------------------------------------------------------------ */ void AQH_ModDevices_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder) { AQH_ModService_Extend(m, sv, baseFolder); AQH_ModService_SetHandleRequestFn(m, _handleRequest); AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule); } int AQH_ModDevices_Create(AQH_SERVICE *sv) { AQH_MODULE *m; int rv; m=AQH_Module_new(); AQH_Module_SetName(m, "devices"); AQH_Module_SetDescr(m, "device module"); AQH_Module_SetGuestPerms(m, 0); _createPermDefList(m); _createRoleList(m); rv=AQH_Service_AddModule(sv, m); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); } AQH_Module_free(m); return rv; } void _createPermDefList(AQH_MODULE *m) { AQH_PERMDEF_LIST *permDefList; permDefList=AQH_PermDef_List_new(); AQH_ModService_AddPermDef(permDefList, "DeviceRead", 0x001, "Read and list devices"); AQH_ModService_AddPermDef(permDefList, "DeviceWrite", 0x002, "Modify devices"); AQH_ModService_AddPermDef(permDefList, "DeviceAdd", 0x004, "Add devices"); AQH_ModService_AddPermDef(permDefList, "DeviceDel", 0x008, "Remove devices"); AQH_ModService_AddPermDef(permDefList, "ValueRead", 0x010, "Read and list values"); AQH_ModService_AddPermDef(permDefList, "ValueWrite", 0x020, "Modify values"); AQH_ModService_AddPermDef(permDefList, "ValueAdd", 0x040, "Add values"); AQH_ModService_AddPermDef(permDefList, "ValueDel", 0x080, "Remove values"); AQH_ModService_AddPermDef(permDefList, "ValueSet", 0x100, "Set values"); AQH_Module_SetPermDefList(m, permDefList); } void _createRoleList(AQH_MODULE *m) { AQH_ROLE_LIST *roleList; int id=0; roleList=AQH_Role_List_new(); AQH_ModService_AddRole(roleList, id++, "Reader", AQH_MODDEVICES_PERMS_DEVICEREAD | AQH_MODDEVICES_PERMS_VALUEREAD, "Read devices and values"); AQH_ModService_AddRole(roleList, id++, "Writer", AQH_MODDEVICES_PERMS_DEVICEREAD | AQH_MODDEVICES_PERMS_DEVICEWRITE | AQH_MODDEVICES_PERMS_DEVICEADD | AQH_MODDEVICES_PERMS_DEVICEDEL | AQH_MODDEVICES_PERMS_VALUEREAD | AQH_MODDEVICES_PERMS_VALUEWRITE | AQH_MODDEVICES_PERMS_VALUEADD | AQH_MODDEVICES_PERMS_VALUEDEL | AQH_MODDEVICES_PERMS_VALUESET, "Read and write devices and values"); AQH_ModService_AddRole(roleList, id++, "Setter", AQH_MODDEVICES_PERMS_DEVICEREAD | AQH_MODDEVICES_PERMS_VALUEREAD | AQH_MODDEVICES_PERMS_VALUESET, "Set values"); AQH_Module_SetRoleList(m, roleList); } AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName) { return NULL; } int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem) { AQH_ModService_HandleRequestWithTable(m, rq, session, sLastPathElem, _requestTable); return AQCGI_SendResponse(rq); } void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, _runIndex, dbuf); } void _handleRqValuesGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, _runValues, dbuf); } void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, _runSetData, dbuf); } void _runIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf) { AQH_DEVICE_LIST *deviceList; AQH_DEVICE *device; uint32_t perms; perms=AQH_ModService_GetUserPerms(m); deviceList=AQH_DataClient_GetDevices(dc, NULL); if (deviceList==NULL) { DBG_ERROR(NULL, "No device received"); GBAS(dbuf, "

No devices.

"); return; } GBAS(dbuf, "

Devices

\n"); GBAS(dbuf, "\n" "\n" "" "" "" "" "" #if 0 "" "" "" "" "" #endif "\n" "\n" "\n"); device=AQH_Device_List_First(deviceList); while(device) { const char *s; GBAA(dbuf, ""); /* name for system */ s=AQH_Device_GetNameForSystem(device); if (perms & AQH_MODDEVICES_PERMS_VALUEREAD) { GBAS(dbuf,"", s?s:""); } else GBAA(dbuf,"", s?s:""); /* room */ s=AQH_Device_GetRoomName(device); GBAA(dbuf, "", s?s:""); /* location */ s=AQH_Device_GetLocation(device); GBAA(dbuf, "", s?s:""); /* description */ s=AQH_Device_GetDescription(device); GBAA(dbuf, "", s?s:""); #if 0 /* device type */ s=AQH_Device_GetDeviceType(device); GBAA(dbuf, "", s?s:""); /* driver */ s=AQH_Device_GetDriver(device); GBAA(dbuf, "", s?s:""); /* short device name */ s=AQH_Device_GetName(device); GBAA(dbuf, "", s?s:""); /* GUI name for device */ s=AQH_Device_GetNameForGui(device); GBAA(dbuf, "", s?s:""); /* manufacturer */ s=AQH_Device_GetManufacturer(device); GBAA(dbuf, "", s?s:""); #endif GBAA(dbuf, ""); device=AQH_Device_List_Next(device); } GBAS(dbuf, "\n" "
Name For SystemRoomLocationDescriptionTypeDriverNameGUI NameManufacturer
%s%s%s%s%s%s%s%s%s%s
\n"); AQH_Device_List_free(deviceList); } void _runValues(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf) { GWEN_DB_NODE *dbQuery; const char *sDeviceName; dbQuery=AQCGI_Request_GetDbQuery(rq); sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL); if (!(sDeviceName && *sDeviceName)) _runIndex(m, rq, session, dc, dbuf); else { AQH_VALUE_LIST *valueList; valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0); if (valueList && AQH_Value_List_GetCount(valueList)) { GBAA(dbuf,"

Values for Device %s

\n", sDeviceName); GBAS(dbuf,"
\n"); GBAA(dbuf, "\n", sDeviceName); _writeValueListToTable(dc, valueList, dbuf); GBAS(dbuf,""); GBAS(dbuf, "
\n\n"); } else { GBAS(dbuf,"

No values.

\n"); } AQH_Value_List_free(valueList); } } void _runSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf) { AQH_SERVICE *sv; GWEN_DB_NODE *dbPost; const char *sDeviceName; AQH_VALUE_LIST *valueList; /* sample data */ sv=AQH_ModService_GetService(m); dbPost=AQCGI_Request_GetDbPostBody(rq); sDeviceName=dbPost?GWEN_DB_GetCharValue(dbPost, "device", 0, NULL):NULL; valueList=sDeviceName?AQH_DataClient_GetValues(dc, sDeviceName, 0):NULL; if (valueList && AQH_Value_List_GetCount(valueList)) { const AQH_VALUE *value; value=AQH_Value_List_First(valueList); while(value) { if (AQH_Value_GetValueType(value)==AQH_ValueType_Actor) { const char *sValueName; const char *sValue; sValueName=AQH_Value_GetName(value); sValue=GWEN_DB_GetCharValue(dbPost, sValueName, 0, NULL); if (sValueName && *sValueName) { DBG_ERROR(NULL, "Setting value %s to %s", sValueName?sValueName:"no name", sValue?sValue:"no value"); switch(AQH_Value_GetModality(value)) { case AQH_ValueModality_RGBW: _setRgbwData(dc, value, sValue); break; case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sValue); break; case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sValue); break; default: break; } /* switch */ } /* if (sValueName) */ } value=AQH_Value_List_Next(value); } /* while */ } AQH_Value_List_free(valueList); if (sDeviceName && *sDeviceName) { GWEN_BUFFER *pbuf; pbuf=GWEN_Buffer_new(0, 256, 0, 1); GBAS(pbuf, "Location: /aqbt/devices/values.html?device="); GWEN_Text_EscapeToBufferTolerant(sDeviceName, pbuf); AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf)); GWEN_Buffer_free(pbuf); } AQCGI_Request_SetResponseCode(rq, 303); AQCGI_Request_SetResponseText(rq, "See other"); } void _writeValueListToTable(AQH_DATACLIENT *dc, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf) { const AQH_VALUE *value; GBAS(dbuf, "\n" "" "" "" "" "" #if 0 "" "" "" #endif "" "" "\n" "\n"); value=AQH_Value_List_First(valueList); while(value) { _writeValueToTable(dc, value, dbuf); value=AQH_Value_List_Next(value); } GBAS(dbuf, "\n" "
NameTypeModalityDriverDeviceName for SystemAction
\n"); } void _writeValueToTable(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf) { const char *s; GBAS(dbuf, ""); s=AQH_Value_GetName(value); GBAA(dbuf, "%s", s?s:""); s=AQH_ValueType_toString(AQH_Value_GetValueType(value)); GBAA(dbuf, "%s", s?s:""); s=AQH_ValueModality_toString(AQH_Value_GetModality(value)); GBAA(dbuf, "%s", s?s:""); #if 0 s=AQH_Value_GetDriver(value); GBAA(dbuf, "%s", s?s:""); s=AQH_Value_GetDeviceNameForSystem(value); GBAA(dbuf, "%s", s?s:""); s=AQH_Value_GetNameForSystem(value); GBAA(dbuf, "%s", s?s:""); #endif GBAS(dbuf, ""); _addLastValueToForm(dc, value, dbuf); GBAS(dbuf, ""); GBAA(dbuf, "\n"); } void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf) { const char *sValueName; sValueName=AQH_Value_GetName(value); switch(AQH_Value_GetModality(value)) { case AQH_ValueModality_RGBW: GBAA(dbuf, "", sValueName); break; case AQH_ValueModality_OnOff: GBAA(dbuf, "", sValueName); break; case AQH_ValueModality_OnOffAuto: GBAA(dbuf, "", sValueName); break; default: break; } } uint32_t _colorFromHexString(const char *s) { uint32_t colorIn=0; uint32_t colorOut; while(*s && *s<33) s++; if (*s=='#') s++; while(*s) { uint c; c=(*s)-'0'; if (c>9) c-=7; colorIn<<=4; colorIn|=c & 0xf; s++; } /* hex 00RRGGBB -> GGRRWWBB */ colorOut=(colorIn & 0x00ff0000) | ((colorIn & 0x00ff00)<<16) | (colorIn & 0x0000ff); return colorOut; } void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue) { if (sValue) { const char *sValueSystemName; uint32_t color; int rv; sValueSystemName=AQH_Value_GetNameForSystem(value); color=_colorFromHexString(sValue); DBG_ERROR(NULL, "Send value [#%08x] to %s", color, sValueSystemName); rv=AQH_DataClient_SetData(dc, value, (double) color); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } } void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue) { if (sValue) { const char *sValueSystemName; int rv; sValueSystemName=AQH_Value_GetNameForSystem(value); if (strcasecmp(sValue, "unchanged")==0) { DBG_ERROR(NULL, "Value %s unchanged", sValueSystemName); } else if (strcasecmp(sValue, "on")==0) { DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName); rv=AQH_DataClient_SetData(dc, value, 1.0); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } else if (strcasecmp(sValue, "off")==0) { DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName); rv=AQH_DataClient_SetData(dc, value, 0.0); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } else { } } } void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue) { if (sValue) { const char *sValueSystemName; int rv; sValueSystemName=AQH_Value_GetNameForSystem(value); if (strcasecmp(sValue, "unchanged")==0) { DBG_ERROR(NULL, "Value %s unchanged", sValueSystemName); } else if (strcasecmp(sValue, "on")==0) { DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName); rv=AQH_DataClient_SetData(dc, value, 1.0); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } else if (strcasecmp(sValue, "off")==0) { DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName); rv=AQH_DataClient_SetData(dc, value, 0.0); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } else if (strcasecmp(sValue, "auto")==0) { DBG_ERROR(NULL, "Send value 2 to %s", sValueSystemName); rv=AQH_DataClient_SetData(dc, value, 2.0); if (rv<0) { DBG_ERROR(NULL, "Error sending data: %d", rv); } } else { DBG_ERROR(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName); } } } void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf) { const char *sValueSystemName; const char *sValueName; uint64_t dataPoints[2]; uint64_t recvdNum; // uint64_t timestamp; union {double f; uint64_t i;} u; int intVal; sValueSystemName=AQH_Value_GetNameForSystem(value); sValueName=AQH_Value_GetName(value); recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1); if (recvdNum>0) { // timestamp=dataPoints[0]; u.i=dataPoints[1]; intVal=(int) u.f; } else { u.i=0; intVal=-1; } if (AQH_Value_GetValueType(value)==AQH_ValueType_Actor) { DBG_ERROR(NULL, "Adding actor"); switch(AQH_Value_GetModality(value)) { case AQH_ValueModality_RGBW: GBAA(dbuf, "", sValueName, (unsigned int) u.f); break; case AQH_ValueModality_OnOff: GBAA(dbuf, ""); break; case AQH_ValueModality_OnOffAuto: GBAA(dbuf, ""); break; default: // GBAA(dbuf, "", sValueName, u.f); GBAA(dbuf, "%.2f", u.f); break; } /* switch */ } /* if actor */ else { DBG_ERROR(NULL, "Adding sensor (%s=%.2f)", sValueName, u.f); GBAA(dbuf, "%.2f", u.f); } }