From fa567fd7e93305227e8389a44c783bb36278f341 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Tue, 25 Aug 2026 23:44:02 +0200 Subject: [PATCH] aqhome-cgi: more work on pages2 - implemented actors - improved page files to allow for more personalization of content --- apps/aqhome-cgi/modules/devices/0BUILD | 2 + apps/aqhome-cgi/modules/devices/mdevices.c | 178 ++++----- apps/aqhome-cgi/modules/devices/mdevices.h | 6 + .../modules/devices/mdevices_page2.c | 115 ++++-- .../modules/devices/mdevices_page2.h | 1 + .../modules/devices/mdevices_page2_actor.c | 347 ++++++++++++++++++ .../modules/devices/mdevices_page2_actor.h | 25 ++ .../modules/devices/mdevices_page2_graph.c | 1 + .../modules/devices/mdevices_pagectx.c | 4 + .../modules/devices/mdevices_pagectx.h | 2 + doc/cgi-examples/0BUILD | 8 +- .../{stube_light.xml => buero_brightness.xml} | 4 +- doc/cgi-examples/buero_climate.xml | 2 +- doc/cgi-examples/buero_light.xml | 77 +--- doc/cgi-examples/stube_climate.xml | 51 --- doc/cgi-examples/stube_hum.xml | 73 ---- doc/cgi-examples/stube_temp.xml | 73 ---- 17 files changed, 560 insertions(+), 409 deletions(-) create mode 100644 apps/aqhome-cgi/modules/devices/mdevices_page2_actor.c create mode 100644 apps/aqhome-cgi/modules/devices/mdevices_page2_actor.h rename doc/cgi-examples/{stube_light.xml => buero_brightness.xml} (96%) delete mode 100644 doc/cgi-examples/stube_climate.xml delete mode 100644 doc/cgi-examples/stube_hum.xml delete mode 100644 doc/cgi-examples/stube_temp.xml diff --git a/apps/aqhome-cgi/modules/devices/0BUILD b/apps/aqhome-cgi/modules/devices/0BUILD index 44fde338..1add5c1b 100644 --- a/apps/aqhome-cgi/modules/devices/0BUILD +++ b/apps/aqhome-cgi/modules/devices/0BUILD @@ -64,6 +64,7 @@ mdevices_page.h mdevices_page2.h mdevices_page2_graph.h + mdevices_page2_actor.h mdevices_pagectx.h mdevices_pagectx_p.h @@ -89,6 +90,7 @@ mdevices_page.c mdevices_page2.c mdevices_page2_graph.c + mdevices_page2_actor.c mdevices_pagectx.c diff --git a/apps/aqhome-cgi/modules/devices/mdevices.c b/apps/aqhome-cgi/modules/devices/mdevices.c index 80d046c5..519e20b9 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices.c +++ b/apps/aqhome-cgi/modules/devices/mdevices.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2025 Martin Preuss, all rights reserved. + * AqHome (c) by 2026 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. @@ -79,9 +79,6 @@ static void _handleRqPageGraphGet(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESS 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); - /* ------------------------------------------------------------------------------------------------ @@ -281,7 +278,7 @@ void _handleRqPageGet(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session void _handleRqPagePost(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, GWEN_BUFFER *dbuf) { - AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPagePost, dbuf); + AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPage2Post, dbuf); } @@ -386,110 +383,6 @@ int AQH_ModDevices_RgbwGetW(uint32_t color) -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; @@ -649,6 +542,23 @@ void AQH_ModDevices_RedirectToDevice(AQCGI_REQUEST *rq, const char *destLocation +void AQH_ModDevices_RedirectToPage(AQCGI_REQUEST *rq, const char *destLocation, const char *sPageId) +{ + GWEN_BUFFER *pbuf; + + pbuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(pbuf, "Location: "); + GWEN_Buffer_AppendString(pbuf, (destLocation && *destLocation)?destLocation:"index.html"); + if (sPageId && *sPageId) { + GWEN_Buffer_AppendString(pbuf, "?page="); + GWEN_Text_EscapeToBuffer(sPageId, pbuf); + } + AQH_ModService_Redirect(rq, GWEN_Buffer_GetStart(pbuf)); + GWEN_Buffer_free(pbuf); +} + + + AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues) { AQDG_GRAPH_DATAPAIR_LIST *dpList; @@ -711,4 +621,54 @@ void AQH_ModDevices_ReadDevicesAndValues(AQCGI_MODULE *m, AQH_DATACLIENT *dc) +GWEN_XMLNODE *AQH_ModDevices_GetXmlTagBelow(GWEN_XMLNODE *node) +{ + if (node) { + GWEN_XMLNODE *n; + + n=GWEN_XMLNode_GetFirstTag(node); + if (n) + return n; + n=GWEN_XMLNode_GetNextTag(node); + if (n) + return n; + n=node; + while(n) { + GWEN_XMLNODE *nParent; + + nParent=GWEN_XMLNode_GetParent(n); + if (nParent==NULL) + return NULL; + n=GWEN_XMLNode_GetNextTag(nParent); + if (n) + return n; + n=nParent; + } + } + return NULL; +} + + + +GWEN_XMLNODE *AQH_ModDevices_FindXmlElement(GWEN_XMLNODE *n, const char *tname, const char *pname, const char *pvalue) +{ + while(n) { + const char *s; + + s=GWEN_XMLNode_GetData(n); + if (s && *s && strcasecmp(s, tname)==0) { + if (pname && *pname && pvalue && *pvalue) { + s=GWEN_XMLNode_GetProperty(n, pname, NULL); + if (s && strcasecmp(s, pvalue)==0) + return n; + } + else + return n; + } + n=AQH_ModDevices_GetXmlTagBelow(n); + } /* while */ + return 0; +} + + diff --git a/apps/aqhome-cgi/modules/devices/mdevices.h b/apps/aqhome-cgi/modules/devices/mdevices.h index 58f78927..74120873 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices.h +++ b/apps/aqhome-cgi/modules/devices/mdevices.h @@ -20,6 +20,8 @@ #include #include +#include + #define AQH_MODDEVICES_PERMS_DEVICEREAD 0x001 @@ -50,6 +52,9 @@ void AQH_ModDevices_SetValueList(AQCGI_MODULE *m, AQH_VALUE_LIST *valueList); void AQH_ModDevices_ReadDevicesAndValues(AQCGI_MODULE *m, AQH_DATACLIENT *dc); +GWEN_XMLNODE *AQH_ModDevices_GetXmlTagBelow(GWEN_XMLNODE *node); +GWEN_XMLNODE *AQH_ModDevices_FindXmlElement(GWEN_XMLNODE *n, const char *tname, const char *pname, const char *pvalue); + uint32_t AQH_ModDevices_ColorFromHexString(const char *s); uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn); @@ -73,6 +78,7 @@ AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, uint64_t tsBegin, uint64_t tsEnd, uint64_t num); void AQH_ModDevices_RedirectToDevice(AQCGI_REQUEST *rq, const char *destLocation, const AQH_DEVICE *device); +void AQH_ModDevices_RedirectToPage(AQCGI_REQUEST *rq, const char *destLocation, const char *sPageId); diff --git a/apps/aqhome-cgi/modules/devices/mdevices_page2.c b/apps/aqhome-cgi/modules/devices/mdevices_page2.c index d5263e44..36b62b7c 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices_page2.c +++ b/apps/aqhome-cgi/modules/devices/mdevices_page2.c @@ -13,6 +13,7 @@ #include "./mdevices_page2.h" #include "./mdevices_page2_graph.h" +#include "./mdevices_page2_actor.h" #include "aqhome-cgi/modules/mdataclient.h" #include "aqhome-cgi/modules/devices/mdevices_pagectx.h" @@ -87,14 +88,16 @@ void AQH_ModDevices_RunPage2Get(AQCGI_MODULE *m, AQCGI_REQUEST *rq, if (sPageId && *sPageId) { GWEN_XMLNODE *fileNode; + DBG_ERROR(NULL, "Reading page \"%s\"", sPageId); fileNode=_readPage(m, sPageId); if (fileNode) { GWEN_XMLNODE *nPage; + DBG_ERROR(NULL, "Read page \"%s\"", sPageId); nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", NULL, NULL); if (nPage) { _writePage(m, dc, nPage, dbuf); - AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120"); + //AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120"); } else { DBG_ERROR(NULL, "No page element in file for \"%s\"", sPageId); @@ -102,7 +105,7 @@ void AQH_ModDevices_RunPage2Get(AQCGI_MODULE *m, AQCGI_REQUEST *rq, GWEN_XMLNode_free(fileNode); } else { - DBG_INFO(NULL, "here"); + DBG_ERROR(NULL, "here"); } } else { @@ -131,6 +134,49 @@ void AQH_ModDevices_RunPage2Graph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, +void AQH_ModDevices_RunPage2Post(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf) +{ + GWEN_DB_NODE *dbPost; + const char *sPageId; + const char *sActorId; + + DBG_INFO(NULL, "RunPagePost"); + dbPost=AQCGI_Request_GetDbPostBody(rq); + sPageId=GWEN_DB_GetCharValue(dbPost, "page", 0, NULL); + sActorId=GWEN_DB_GetCharValue(dbPost, "actor", 0, NULL); + if (sPageId && *sPageId && sActorId && *sActorId) { + GWEN_XMLNODE *fileNode; + + fileNode=_readPage(m, sPageId); + if (fileNode) { + GWEN_XMLNODE *nPage; + GWEN_XMLNODE *nActor; + + nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId); + nActor=AQH_ModDevices_FindXmlElement(nPage, "actor", "id", sActorId); + if (nPage && nActor) { + AQH_MD_PAGECTX *ctx; + + ctx=_createInitialCtx(m, dc); + AQH_ModDevices_HandlePageActor(ctx, rq, sActorId, nActor); + AQH_MDPageCtx_free(ctx); + } + else { + DBG_ERROR(NULL, "Actor %s/%s not found", sPageId, sActorId); + } + GWEN_XMLNode_free(fileNode); + } + else { + DBG_INFO(NULL, "here"); + } + } + + AQH_ModDevices_RedirectToPage(rq, DOC_PAGE2, sPageId); +} + + + + void AQH_ModDevices_SetCharValue(GWEN_DB_NODE *dbDest, uint32_t flags, const char *sVar, const char *sValue, GWEN_DB_NODE *dbContext) { if (sValue && *sValue) { @@ -361,10 +407,17 @@ void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n sElementName=GWEN_XMLNode_GetData(nElement); if (sElementName && *sElementName) { + DBG_ERROR(NULL, "Reading element \"%s\"", sElementName); if (strcasecmp(sElementName, "dataset")==0) _writePageDataSet(ctx, sPageId, nElement, dbuf); else if (strcasecmp(sElementName, "item")==0) _writePageItem(ctx, sPageId, nElement, dbuf); + else if (strcasecmp(sElementName, "graph")==0) + AQH_ModDevices_PageWriteGraph(ctx, sPageId, nElement, dbuf); + else if (strcasecmp(sElementName, "actor")==0) + AQH_ModDevices_PageWriteActor(ctx, sPageId, nElement, dbuf); +// else if (strcasecmp(sName, "valueTable")==0) +// _writePageValueTable(ctx, sPageId, n, dbuf); else if (strcasecmp(sElementName, "forEveryValue")==0) _writePageForEveryValue(ctx, sPageId, nElement, dbuf); else if (strcasecmp(sElementName, "table")==0 || @@ -372,6 +425,7 @@ void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n strcasecmp(sElementName, "td")==0 || strcasecmp(sElementName, "p")==0) _writeHtmlElement(ctx, sPageId, nElement, dbuf); + DBG_ERROR(NULL, "Done with element \"%s\"", sElementName); } } else if (GWEN_XMLNode_GetType(nElement)==GWEN_XMLNodeTypeData) { @@ -383,7 +437,7 @@ void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n GWEN_BUFFER *tbuf; tbuf=GWEN_Buffer_new(0, 256, 0, 1); - GBAS(tbuf, s); + GWEN_DB_ReplaceVars(AQH_MDPageCtx_GetDbVars(ctx), s, tbuf); GWEN_Text_CondenseBuffer(tbuf); GBAS(dbuf, GWEN_Buffer_GetStart(tbuf)); GWEN_Buffer_free(tbuf); @@ -396,14 +450,30 @@ void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n void _writeHtmlElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf) { const char *sElementName; - const char *sClass; sElementName=GWEN_XMLNode_GetData(nElement); - sClass=GWEN_XMLNode_GetProperty(nElement, "class", NULL); /* TODO: copy all attributes */ - if (sClass) - GBAA(dbuf, "<%s class=\"%s\">", sElementName, sClass); - else - GBAA(dbuf, "<%s>", sElementName); + if (sElementName) { /* ugly hack because GWEN_XML does not expose raw attributes */ + GWEN_XMLNODE *newRoot; + GWEN_XMLNODE *newElement; + GWEN_BUFFER *tbuf; + char *p; + + newRoot=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "root"); + newElement=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, sElementName); + GWEN_XMLNode_AddChild(newRoot, newElement); + GWEN_XMLNode_CopyProperties(newElement, nElement, 0); + GWEN_XMLNode_ExpandProperties(newElement, AQH_MDPageCtx_GetDbVars(ctx)); + tbuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_XMLNode_toBuffer(newRoot, tbuf, GWEN_XML_FLAGS_SIMPLE); + DBG_ERROR(NULL, "XML: %s", GWEN_Buffer_GetStart(tbuf)); + p=strchr(GWEN_Buffer_GetStart(tbuf), '>'); + if (p && *p) { + p[1]=0; + GBAS(dbuf, GWEN_Buffer_GetStart(tbuf)); + } + GWEN_Buffer_free(tbuf); + } + _writePageChildElements(ctx, sPageId, nElement, dbuf); GBAA(dbuf, "", sElementName); } @@ -463,34 +533,13 @@ void _writePageDataSet(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n void _writePageItem(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf) { - GWEN_XMLNODE *n; const char *sTitle; /* write title */ sTitle=GWEN_XMLNode_GetProperty(nElement, "title", NULL); if (sTitle && *sTitle) GBAA(dbuf, "

%s

\n", sTitle); - - /* write real element */ - n=GWEN_XMLNode_GetChild(nElement); - while(n) { - const char *sName; - - sName=GWEN_XMLNode_GetData(n); - if (sName && *sName) { - if (strcasecmp(sName, "graph")==0) - AQH_ModDevices_PageWriteGraph(ctx, sPageId, n, dbuf); -// else if (strcasecmp(sName, "actor")==0) -// _writePageActor(ctx, sPageId, n, dbuf); -// else if (strcasecmp(sName, "valueTable")==0) -// _writePageValueTable(ctx, sPageId, n, dbuf); - else { - /* unhandled */ - } - } - - n=GWEN_XMLNode_Next(n); - } + _writePageChildElements(ctx, sPageId, nElement, dbuf); } @@ -516,7 +565,3 @@ AQH_MD_PAGECTX *_createInitialCtx(AQCGI_MODULE *m, AQH_DATACLIENT *dc) - - - - diff --git a/apps/aqhome-cgi/modules/devices/mdevices_page2.h b/apps/aqhome-cgi/modules/devices/mdevices_page2.h index 8afa3180..9b307091 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices_page2.h +++ b/apps/aqhome-cgi/modules/devices/mdevices_page2.h @@ -23,6 +23,7 @@ void AQH_ModDevices_RunPage2Get(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); void AQH_ModDevices_RunPage2Graph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); +void AQH_ModDevices_RunPage2Post(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf); void AQH_ModDevices_SetCharValue(GWEN_DB_NODE *dbDest, uint32_t flags, const char *sVar, const char *sValue, GWEN_DB_NODE *dbCtx); void AQH_ModDevices_SetupDbForDeviceAndValue(const AQH_VALUE *v, GWEN_DB_NODE *dbCtx); diff --git a/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.c b/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.c new file mode 100644 index 00000000..10114aa4 --- /dev/null +++ b/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.c @@ -0,0 +1,347 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2026 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_page2_actor.h" + +#include + +#include +#include +#include + + +#include +#include + + +#define DOC_PAGE2 "page.html" + + + + + +/* ------------------------------------------------------------------------------------------------ + * defs and enums + * ------------------------------------------------------------------------------------------------ + */ + +#define GBAS GWEN_Buffer_AppendString +#define GBAA GWEN_Buffer_AppendArgs + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _writeIntToForm(const char *sValueName, int v, GWEN_BUFFER *dbuf); +static void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf); +static void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf); +static void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf); + +static void _setIntData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue); +static void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value); +static int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue); +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); + + + +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ + + + +void AQH_ModDevices_PageWriteActor(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n, GWEN_BUFFER *dbuf) +{ + const char *sActorId; + const char *sValueName; + const char *sLabel; + + sActorId=GWEN_XMLNode_GetProperty(n, "id", NULL); + sLabel=GWEN_XMLNode_GetProperty(n, "label", NULL); + sValueName=GWEN_XMLNode_GetProperty(n, "value", NULL); + DBG_ERROR(NULL, "Writing page for actor \"%s\"", sValueName); + if (sActorId && *sActorId && sValueName && *sValueName) { + AQH_VALUE *value; + + value=AQH_MDPageCtx_GetValue(ctx, sValueName); + if (value) { + uint32_t lastData; + + lastData=AQH_ModDevices_ValueGetLastDataAsUint32(AQH_MDPageCtx_GetDataClient(ctx), value, 0); + GBAS(dbuf,"
\n"); + GBAA(dbuf, "\n", sPageId); + GBAA(dbuf, "\n", sActorId); + + if (sLabel && *sLabel) + GBAA(dbuf,"", sActorId, sLabel); + switch(AQH_Value_GetModality(value)) { + case AQH_ValueModality_RGBW: _writeRgbwToForm(sActorId, lastData, dbuf); break; + case AQH_ValueModality_OnOff: _writeOnOffToForm(sActorId, lastData, dbuf); break; + case AQH_ValueModality_OnOffAuto: _writeOnOffAutoToForm(sActorId, lastData, dbuf); break; + default: _writeIntToForm(sActorId, lastData, dbuf); break; + } /* switch */ + + GBAS(dbuf,""); + GBAS(dbuf, "
\n\n"); + } + } +} + + + +/* TODO: share this with mdevices_value!! */ + + + +void _writeIntToForm(const char *sValueName, int v, GWEN_BUFFER *dbuf) +{ + GBAA(dbuf, "", sValueName, v); +} + + + +void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf) +{ +#if 1 + DBG_ERROR(NULL, "Color=%08x (%d, %d, %d, %d)", + color, + AQH_ModDevices_RgbwGetR(color), + AQH_ModDevices_RgbwGetG(color), + AQH_ModDevices_RgbwGetB(color), + AQH_ModDevices_RgbwGetW(color)); + GBAA(dbuf, "", sValueName); + GBAA(dbuf, "", + sValueName, sValueName, AQH_ModDevices_RgbwGetR(color)); + + GBAA(dbuf, "", sValueName); + GBAA(dbuf, "", + sValueName, sValueName, AQH_ModDevices_RgbwGetG(color)); + + GBAA(dbuf, "", sValueName); + GBAA(dbuf, "", + sValueName, sValueName, AQH_ModDevices_RgbwGetB(color)); + + GBAA(dbuf, "", sValueName); + GBAA(dbuf, "", + sValueName, sValueName, AQH_ModDevices_RgbwGetW(color)); + +#else + GBAA(dbuf, "", sValueName, sValueName, color); + // else + GBAA(dbuf, "#%08x (#%08x)", + sValueName, sValueName, + AQH_ModDevices_RgbwToHtmlColor(color), + AQH_ModDevices_RgbwToHtmlColor(color), + color); +#endif +} + + + +void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf) +{ + GBAA(dbuf, ""); +} + + + +void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf) +{ + GBAA(dbuf, ""); +} + + + +void AQH_ModDevices_HandlePageActor(AQH_MD_PAGECTX *ctx, AQCGI_REQUEST *rq, const char *sActorId, GWEN_XMLNODE *nActor) +{ + AQH_DATACLIENT *dc; + GWEN_DB_NODE *dbPost; + const char *sValueName; + + dc=AQH_MDPageCtx_GetDataClient(ctx); + dbPost=AQCGI_Request_GetDbPostBody(rq); + sValueName=GWEN_XMLNode_GetProperty(nActor, "value", NULL); + + if (sValueName && *sValueName) { + AQH_VALUE *value; + + value=AQH_MDPageCtx_GetValue(ctx, sValueName); + if (value) { + const char *sData; + + sData=GWEN_DB_GetCharValue(dbPost, sActorId, 0, NULL); + DBG_INFO(NULL, "Setting value %s to %s", sValueName, sData?sData:"empty/no value"); + switch(AQH_Value_GetModality(value)) { + case AQH_ValueModality_RGBW: _setRgbwData(dc, dbPost, sActorId, value); break; + case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sData); break; + case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sData); break; + default: _setIntData(dc, value, sData); break; + } /* switch */ + } + } +} + + + +void _setIntData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue) +{ + if (sValue) { + const char *sValueSystemName; + int intVal; + int rv; + + sValueSystemName=AQH_Value_GetNameForSystem(value); + if (1==sscanf(sValue, "%d", &intVal)) { + DBG_ERROR(NULL, "Send value [%d] to %s", intVal, sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, (double) intVal); + if (rv<0) { + DBG_ERROR(NULL, "Error sending data: %d", rv); + } + } + else { + DBG_ERROR(NULL, "Not an integer value: [%s]", sValue); + } + } +} + + + +void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value) +{ + const char *sValueSystemName; + uint32_t color; + int rv; + + sValueSystemName=AQH_Value_GetNameForSystem(value); + DBG_INFO(NULL, "Set value %s", sValueName); + color=AQH_ModDevices_RgbwFromComponents(_getColorComponent(dbPost, sValueName, "r", 0), + _getColorComponent(dbPost, sValueName, "g", 0), + _getColorComponent(dbPost, sValueName, "b", 0), + _getColorComponent(dbPost, sValueName, "w", 0)); + DBG_INFO(NULL, "Send value [#%08x] to %s", color, sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, (double) color); + if (rv<0) { + DBG_INFO(NULL, "Error sending data: %d", rv); + } +} + + + +int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue) +{ + GWEN_BUFFER *buf; + const char *sData; + int result; + + buf=GWEN_Buffer_new(0, 64, 0, 1); + GBAA(buf, "%s_%s", sValueName, sComponent); + DBG_INFO(NULL, "Read value %s", GWEN_Buffer_GetStart(buf)); + sData=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(buf), 0, NULL); + GWEN_Buffer_free(buf); + if (sData) { + if (1==sscanf(sData, "%u", &result)) + return result; + } + return defaultValue; +} + + + +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_INFO(NULL, "Value %s unchanged", sValueSystemName); + } + else if (strcasecmp(sValue, "on")==0) { + DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, 1.0); + if (rv<0) { + DBG_INFO(NULL, "Error sending data: %d", rv); + } + } + else if (strcasecmp(sValue, "off")==0) { + DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, 0.0); + if (rv<0) { + DBG_INFO(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_INFO(NULL, "Value %s unchanged", sValueSystemName); + } + else if (strcasecmp(sValue, "on")==0) { + DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, 1.0); + if (rv<0) { + DBG_INFO(NULL, "Error sending data: %d", rv); + } + } + else if (strcasecmp(sValue, "off")==0) { + DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, 0.0); + if (rv<0) { + DBG_INFO(NULL, "Error sending data: %d", rv); + } + } + else if (strcasecmp(sValue, "auto")==0) { + DBG_INFO(NULL, "Send value 2 to %s", sValueSystemName); + rv=AQH_DataClient_SetData(dc, value, 2.0); + if (rv<0) { + DBG_INFO(NULL, "Error sending data: %d", rv); + } + } + else { + DBG_INFO(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName); + } + } +} + + + + + + + + + diff --git a/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.h b/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.h new file mode 100644 index 00000000..2e35119f --- /dev/null +++ b/apps/aqhome-cgi/modules/devices/mdevices_page2_actor.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2026 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. + ****************************************************************************/ + +#ifndef AQHOME_CGI_MDEVICES_PAGE2_ACTOR_H +#define AQHOME_CGI_MDEVICES_PAGE2_ACTOR_H + + +#include "aqhome-cgi/modules/devices/mdevices_page2.h" +#include "aqhome-cgi/modules/devices/mdevices_pagectx.h" + +#include + + + +void AQH_ModDevices_PageWriteActor(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n, GWEN_BUFFER *dbuf); +void AQH_ModDevices_HandlePageActor(AQH_MD_PAGECTX *ctx, AQCGI_REQUEST *rq, const char *sActorId, GWEN_XMLNODE *nActor); + + + +#endif diff --git a/apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c b/apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c index 32f7b3b7..39057005 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c +++ b/apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c @@ -279,6 +279,7 @@ void AQH_ModDevices_HandleGraph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIE DBG_INFO(NULL, "Generating graph \"%s\"", GWEN_Buffer_GetStart(nbuf)); _genPageGraph(dc, dbGraph, GWEN_Buffer_GetStart(nbuf)); } + AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120"); AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(nbuf), "image/png", dbuf); } GWEN_DB_Group_free(dbGraph); diff --git a/apps/aqhome-cgi/modules/devices/mdevices_pagectx.c b/apps/aqhome-cgi/modules/devices/mdevices_pagectx.c index 0e4c8483..273395a0 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices_pagectx.c +++ b/apps/aqhome-cgi/modules/devices/mdevices_pagectx.c @@ -203,6 +203,10 @@ AQH_DATACLIENT *AQH_MDPageCtx_GetDataClient(const AQH_MD_PAGECTX *ctx) +AQH_VALUE *AQH_MDPageCtx_GetValue(const AQH_MD_PAGECTX *ctx, const char *vName) +{ + return (ctx && ctx->valueList && vName)?AQH_Value_List_GetByNameForSystem(ctx->valueList, vName):NULL; +} diff --git a/apps/aqhome-cgi/modules/devices/mdevices_pagectx.h b/apps/aqhome-cgi/modules/devices/mdevices_pagectx.h index 6b6b791a..44337e76 100644 --- a/apps/aqhome-cgi/modules/devices/mdevices_pagectx.h +++ b/apps/aqhome-cgi/modules/devices/mdevices_pagectx.h @@ -47,6 +47,8 @@ void AQH_MDPageCtx_SetValues(AQH_MD_PAGECTX *ctx, AQH_VALUE_LIST *vList); GWEN_DB_NODE *AQH_MDPageCtx_GetDbVars(const AQH_MD_PAGECTX *ctx); +AQH_VALUE *AQH_MDPageCtx_GetValue(const AQH_MD_PAGECTX *ctx, const char *vName); + diff --git a/doc/cgi-examples/0BUILD b/doc/cgi-examples/0BUILD index 99abf623..af31660a 100644 --- a/doc/cgi-examples/0BUILD +++ b/doc/cgi-examples/0BUILD @@ -4,14 +4,10 @@ buero_climate.xml + buero_brightness.xml buero_hum.xml - buero_light.xml buero_temp.xml - - stube_climate.xml - stube_hum.xml - stube_light.xml - stube_temp.xml + buero_light.xml diff --git a/doc/cgi-examples/stube_light.xml b/doc/cgi-examples/buero_brightness.xml similarity index 96% rename from doc/cgi-examples/stube_light.xml rename to doc/cgi-examples/buero_brightness.xml index ddc9b25d..ae43e5aa 100644 --- a/doc/cgi-examples/stube_light.xml +++ b/doc/cgi-examples/buero_brightness.xml @@ -1,5 +1,5 @@ - - + +
diff --git a/doc/cgi-examples/buero_climate.xml b/doc/cgi-examples/buero_climate.xml index 6dd4115e..3b1b5c0e 100644 --- a/doc/cgi-examples/buero_climate.xml +++ b/doc/cgi-examples/buero_climate.xml @@ -35,7 +35,7 @@ - diff --git a/doc/cgi-examples/buero_light.xml b/doc/cgi-examples/buero_light.xml index 303e505f..cb0e560a 100644 --- a/doc/cgi-examples/buero_light.xml +++ b/doc/cgi-examples/buero_light.xml @@ -1,73 +1,32 @@ - - + + + + - + - - - -
Deckenlicht Modus - - - - - - - - - - - - - - - +
Deckenlicht Farbe - - - - - - - - - - - - - - - +
- - - - - - - - - - - - - - - + + + +
-
+
diff --git a/doc/cgi-examples/stube_climate.xml b/doc/cgi-examples/stube_climate.xml deleted file mode 100644 index 778dee10..00000000 --- a/doc/cgi-examples/stube_climate.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
-
-
diff --git a/doc/cgi-examples/stube_hum.xml b/doc/cgi-examples/stube_hum.xml deleted file mode 100644 index 184b42a5..00000000 --- a/doc/cgi-examples/stube_hum.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
-
-
diff --git a/doc/cgi-examples/stube_temp.xml b/doc/cgi-examples/stube_temp.xml deleted file mode 100644 index d66773f3..00000000 --- a/doc/cgi-examples/stube_temp.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
-
-