aqhome-cgi: more work on pages2

- implemented actors
- improved page files to allow for more personalization of content
This commit is contained in:
Martin Preuss
2026-08-25 23:44:02 +02:00
parent d47a496c3f
commit fa567fd7e9
17 changed files with 560 additions and 409 deletions

View File

@@ -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
</headers>
@@ -89,6 +90,7 @@
mdevices_page.c
mdevices_page2.c
mdevices_page2_graph.c
mdevices_page2_actor.c
mdevices_pagectx.c
</sources>

View File

@@ -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, "<input type=\"color\" name=\"%s\" value=\"refresh\"/>", sValueName);
break;
case AQH_ValueModality_OnOff:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"</select>",
sValueName);
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"<option value=\"auto\">auto</option>"
"</select>",
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, "<input type=\"text\" name=\"%s\" value=\"#%08x\"/>", sValueName, (uint32_t) (u.f));
#else
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"#%08x\"/>#%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, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
break;
default:
// GBAA(dbuf, "<input type=\"text\" name=\"%s\" value=\"%.2f\"/>", 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;
}

View File

@@ -20,6 +20,8 @@
#include <aqdiagram/graph/datapair.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/xml.h>
#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);

View File

@@ -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, "</%s>", 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, "<h2>%s</h2>\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)

View File

@@ -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);

View File

@@ -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 <config.h>
#endif
#include "./mdevices_page2_actor.h"
#include <aqcgi/service/module.h>
#include <aqdiagram/graph/timegraph.h>
#include <aqdiagram/graph/w_graph.h>
#include <aqdiagram/draw/context_cairo.h>
#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>
#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,"<form action=\""DOC_PAGE2"\" method=\"post\">\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"page\" value=\"%s\">\n", sPageId);
GBAA(dbuf, "<input type=\"hidden\" name=\"actor\" value=\"%s\">\n", sActorId);
if (sLabel && *sLabel)
GBAA(dbuf,"<label for=\"%s\">%s</label>", 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,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
GBAS(dbuf, "</form>\n\n");
}
}
}
/* TODO: share this with mdevices_value!! */
void _writeIntToForm(const char *sValueName, int v, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<input type=\"number\" name=\"%s\" value=\"%d\"/>", 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, "<label for=\"%s_r\">R:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_r\" id=name=\"%s_r\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetR(color));
GBAA(dbuf, "<label for=\"%s_g\">G:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_g\" id=name=\"%s_g\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetG(color));
GBAA(dbuf, "<label for=\"%s_b\">B:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_b\" id=name=\"%s_b\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetB(color));
GBAA(dbuf, "<label for=\"%s_w\">W:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_w\" id=name=\"%s_w\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetW(color));
#else
GBAA(dbuf, "<input type=\"text\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>", sValueName, sValueName, color);
// else
GBAA(dbuf, "<input type=\"color\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>#%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, "<select name=\"%s\" id=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
}
void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<select name=\"%s\" id=\"%s\" >" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
}
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);
}
}
}

View File

@@ -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 <gwenhywfar/xml.h>
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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -4,14 +4,10 @@
<data dist="true">
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
</data>
<subdirs>

View File

@@ -1,5 +1,5 @@
<page id="stube_light" title="Helligkeit im Wohnzimmer">
<dataset room="Stube" modality="Light">
<page id="buero_brightness" title="Helligkeit im Buero">
<dataset room="Buero" modality="Light">
<table>
<tr>
<td>

View File

@@ -35,7 +35,7 @@
<dataset modality="Light">
<td>
<item >
<graph id="light4h" title="Light (last 4h)" link="page.html?page=buero_light"
<graph id="light4h" title="Light (last 4h)" link="page.html?page=buero_brightness"
width="640" height="480" begin="-4h" end="0" refreshtime="120" lowerLimit="-5.0" upperLimit="1034.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />

View File

@@ -1,73 +1,32 @@
<page id="buero_light" title="Helligkeit im Buero">
<dataset room="Buero" modality="Light">
<page id="buero_light" title="Buero" >
<item title="Buero Deckenlicht" >
<table>
<tr>
<td>Deckenlicht Modus</td>
<td>
<item>
<graph id="light4h" title="Letzten 4 Stunden"
width="640" height="480" begin="-4h" end="0" refreshtime="180" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="light12h" title="Letzten 12 Stunden"
width="640" height="480" begin="-12h" end="0" refreshtime="300" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm20" />
</ForEveryValue>
</graph>
</item>
<actor id="buero_ledstrip1_mode" value="nodes/XXXXXXXX/LIGHTCTL_MODE" />
</td>
</tr>
<tr>
<td>Deckenlicht Farbe</td>
<td>
<item>
<graph id="light24h" title="Letzten 24 Stunden"
width="640" height="480" begin="-24h" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm30" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="light7d" title="Letzten 7 Tagen"
width="640" height="480" begin="-7d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm210" />
</ForEveryValue>
</graph>
</item>
<actor id="buero_ledstrip1_rgbw" value="nodes/XXXXXXXX/LIGHTCTL_VALUE" />
</td>
</tr>
<tr>
<td>
<item>
<graph id="light30d" title="Letzten 30 Tage"
width="640" height="480" begin="-30d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm840" />
</ForEveryValue>
<td colspan="2">
<graph id="buero_ledstrip1_state_8h"
title="Buero Deckenlicht"
width="640"
height="480"
begin="-8h"
end="0"
precision="2">
<curve title="Buero" value="nodes/XXXXXXXX/LIGHTCTL_STATE" modifier="Sa30" />
</graph>
</item>
</td>
<td>
<item>
<graph id="light12m" title="Letzten 12 Monate"
width="640" height="480" begin="-365d" end="0" refreshtime="3600" lowerLimit="-5.0" upperLimit="1034" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10080" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
</table>
</dataset>
</item>
</page>

View File

@@ -1,51 +0,0 @@
<page id="stube_climate" title="Klima im Wohnzimmer">
<dataset room="Stube">
<table>
<tr>
<dataset modality="Temperature">
<td>
<item>
<graph id="temp4h" title="Temperature (last 4h)" link="page.html?page=stube_temp"
width="640" height="480" begin="-4h" end="0" refreshtime="180" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
</dataset>
</tr>
<tr>
<dataset modality="Humidity">
<td>
<item >
<graph id="hum4h" title="Humidity (last 4h)" link="page.html?page=stube_hum"
width="640" height="480" begin="-4h" end="0" refreshtime="180" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
</dataset>
</tr>
<tr>
<dataset modality="Light">
<td>
<item >
<graph id="light4h" title="Light (last 4h)" link="page.html?page=stube_light"
width="640" height="480" begin="-4h" end="0" refreshtime="120" lowerLimit="-5.0" upperLimit="1034.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
</dataset>
</tr>
</table>
</dataset>
</page>

View File

@@ -1,73 +0,0 @@
<page id="stube_hum" title="Luftfeuchtigkeit im Wohnzimmer">
<dataset room="Stube" modality="Humidity">
<table>
<tr>
<td>
<item>
<graph id="hum4h" title="Letzten 4 Stunden"
width="640" height="480" begin="-4h" end="0" refreshtime="180" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="hum12h" title="Letzten 12 Stunden"
width="640" height="480" begin="-12h" end="0" refreshtime="300" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm20" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
<tr>
<td>
<item>
<graph id="hum24h" title="Letzten 24 Stunden"
width="640" height="480" begin="-24h" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm30" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="hum7d" title="Letzten 7 Tagen"
width="640" height="480" begin="-7d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm210" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
<tr>
<td>
<item>
<graph id="hum30d" title="Letzten 30 Tage"
width="640" height="480" begin="-30d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm840" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="hum12m" title="Letzten 12 Monate"
width="640" height="480" begin="-365d" end="0" refreshtime="3600" lowerLimit="-5.0" upperLimit="110.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10080" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
</table>
</dataset>
</page>

View File

@@ -1,73 +0,0 @@
<page id="stube_temp" title="Temperaturen im Wohnzimmer">
<dataset room="Stube" modality="Temperature">
<table>
<tr>
<td>
<item>
<graph id="temp4h" title="Letzten 4 Stunden"
width="640" height="480" begin="-4h" end="0" refreshtime="180" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="temp12h" title="Letzten 12 Stunden"
width="640" height="480" begin="-12h" end="0" refreshtime="300" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm20" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
<tr>
<td>
<item>
<graph id="temp24h" title="Letzten 24 Stunden"
width="640" height="480" begin="-24h" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm30" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="temp7d" title="Letzten 7 Tagen"
width="640" height="480" begin="-7d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm210" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
<tr>
<td>
<item>
<graph id="temp30d" title="Letzten 30 Tage"
width="640" height="480" begin="-30d" end="0" refreshtime="600" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm840" />
</ForEveryValue>
</graph>
</item>
</td>
<td>
<item>
<graph id="temp12m" title="Letzten 12 Monate"
width="640" height="480" begin="-365d" end="0" refreshtime="3600" lowerLimit="-5.0" upperLimit="30.0" >
<ForEveryValue>
<curve title="$(Device/descriptiveName)" value="$(Value/NameForSystem)" modifier="Lm10080" />
</ForEveryValue>
</graph>
</item>
</td>
</tr>
</table>
</dataset>
</page>