/**************************************************************************** * 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/modules/devices/mdevices_index.h" #include "aqhome-cgi/modules/devices/mdevices_valuestable.h" #include "aqhome-cgi/modules/devices/mdevices_valuesgraph.h" #include "aqhome-cgi/modules/devices/mdevices_value.h" #include "aqhome-cgi/modules/devices/mdevices_setdata.h" #include "aqhome-cgi/modules/devices/mdevices_vgraph.h" #include "aqhome-cgi/modules/devices/mdevices_device.h" #include "aqhome-cgi/modules/devices/mdevices_setdevice.h" #include "aqhome-cgi/modules/devices/mdevices_page.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_DEVICEWRITE AQH_MODDEVICES_PERMS_DEVICEWRITE #define P_VALUEREAD AQH_MODDEVICES_PERMS_VALUEREAD #define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ 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 _handleRqValuesTableGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqValuesGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqValueGet(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 _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf); static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues); static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf); 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}, {"device.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqDeviceGet}, {"device.html", AQCGI_REQUEST_METHOD_POST, P_DEVICEWRITE, _handleRqDevicePost}, {"vtable.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesTableGet}, {"vgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesGraphGet}, {"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet}, {"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost}, {"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet}, {"page.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGet}, {"page.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqPagePost}, {"pgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGraphGet}, {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); } 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, AQH_ModDevices_RunIndex, dbuf); } void _handleRqValuesTableGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValuesAsTable, dbuf); } void _handleRqValuesGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValuesAsGraph, dbuf); } void _handleRqValueGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValue, dbuf); } void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunSetData, dbuf); } void _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunGraphValue, dbuf); } void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunDevice, dbuf); } void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunSetDevice, dbuf); } void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGet, dbuf); } void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPagePost, dbuf); } void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf) { AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGraph, dbuf); } uint32_t AQH_ModDevices_ColorFromHexString(const char *s) { uint32_t colorIn=0; 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 */ //return _htmlColorToValueRGBW(colorIn); return colorIn; } uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn) { uint32_t colorOut; /* hex 00RRGGBB -> GGRRWWBB */ /* RGBW GGRRWWBB GGRRWWBB GGRRWWBB */ /* html 00RRGGBB 00RRGGBB 00RRGGBB*/ colorOut=(colorIn & 0x00ff0000) | ((colorIn<<16) & 0xff000000) | (colorIn & 0x000000ff); return colorOut; } uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn) { uint32_t colorOut; /* RGBW GGRRWWBB GGRRWWBB GGRRWWBB */ /* hex 00RRGGBB 00RRGGBB 00RRGGBB*/ colorOut=(colorIn & 0x00ff0000) | ( (colorIn>>16) & 0x0000ff00) | (colorIn & 0x000000ff); return colorOut; } uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w) { uint32_t colorOut; colorOut=((r & 0xff)<<16) | ((g & 0xff)<<24) | (b & 0xff) | ((w & 0xff)<<8); return colorOut; } int AQH_ModDevices_RgbwGetR(uint32_t color) { /* GGRRWWBB */ return (color & 0x00ff0000)>>16; } int AQH_ModDevices_RgbwGetG(uint32_t color) { /* GGRRWWBB */ return (color & 0xff000000)>>24; } int AQH_ModDevices_RgbwGetB(uint32_t color) { /* GGRRWWBB */ return (color & 0x000000ff); } int AQH_ModDevices_RgbwGetW(uint32_t color) { /* GGRRWWBB */ return (color & 0x0000ff00)>>8; } 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; } } 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: DBG_ERROR(NULL, "Color: %.f RGBW=%08x HTML=%08x, RGBW2=%08x", u.f, (uint32_t) (u.f), AQH_ModDevices_RgbwToHtmlColor(u.f), AQH_ModDevices_HtmlColorToValueRGBW(AQH_ModDevices_RgbwToHtmlColor(u.f))); #if 1 GBAA(dbuf, "", sValueName, (uint32_t) (u.f)); #else GBAA(dbuf, "#%08x (#%08x)", sValueName, _rgbwToHtmlColor((unsigned int) (u.f)), _rgbwToHtmlColor((unsigned int) (u.f)), (uint32_t) (u.f)); #endif 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); } } AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName) { AQH_VALUE_LIST *valueList; valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0); if (valueList) { AQH_VALUE *value; value=AQH_Value_List_First(valueList); while(value) { const char *s; s=AQH_Value_GetName(value); if (s && *s && strcasecmp(s, sValueName)==0) { break; } value=AQH_Value_List_Next(value); } if (value) { AQH_Value_List_Del(value); AQH_Value_List_free(valueList); return value; } AQH_Value_List_free(valueList); } return NULL; } AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName) { AQH_DEVICE_LIST *deviceList; deviceList=AQH_DataClient_GetDevices(dc, sDeviceName); if (deviceList) { AQH_DEVICE *device; device=AQH_Device_List_First(deviceList); while(device) { const char *s; s=AQH_Device_GetNameForSystem(device); if (s && *s && 0==strcasecmp(s, sDeviceName)) { AQH_Device_List_Del(device); AQH_Device_List_free(deviceList); return device; } device=AQH_Device_List_Next(device); } AQH_Device_List_free(deviceList); } return NULL; } int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue) { const char *sValueSystemName; uint64_t dataPoints[2]; uint64_t recvdNum; // uint64_t timestamp; union {double f; uint64_t i;} u; int intVal; sValueSystemName=AQH_Value_GetNameForSystem(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 { DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName); intVal=defaultValue; } return intVal; } uint32_t AQH_ModDevices_ValueGetLastDataAsUint32(AQH_DATACLIENT *dc, const AQH_VALUE *value, uint32_t defaultValue) { const char *sValueSystemName; uint64_t dataPoints[2]; uint64_t recvdNum; // uint64_t timestamp; union {double f; uint64_t i;} u; uint32_t uintVal; sValueSystemName=AQH_Value_GetNameForSystem(value); recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1); if (recvdNum>0) { // timestamp=dataPoints[0]; u.i=dataPoints[1]; uintVal=(uint32_t) u.f; DBG_ERROR(NULL, "Transformed value %.2f -> %08x", u.f, uintVal); } else { DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName); uintVal=defaultValue; } return uintVal; } AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num) { uint64_t *dataPoints; uint64_t recvdNum; dataPoints=malloc(num*sizeof(uint64_t)*2); recvdNum=AQH_DataClient_GetPeriodData(dc, systemValueName, dataPoints, num, tsBegin, tsEnd); if (recvdNum>0) { AQDG_GRAPH_DATAPAIR_LIST *dpList; dpList=_createDataPairListFromDataPoints(dataPoints, recvdNum); free(dataPoints); return dpList; } else { DBG_ERROR(NULL, "No data received for %s", systemValueName); free(dataPoints); return NULL; } } AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues) { AQDG_GRAPH_DATAPAIR_LIST *dpList; uint64_t i; DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues); dpList=AQDG_Graph_DataPair_List_new(); for(i=0; i