aqhome-cgi: rewrite of the page module.
This commit is contained in:
@@ -62,6 +62,10 @@
|
||||
mdevices_device.h
|
||||
mdevices_setdevice.h
|
||||
mdevices_page.h
|
||||
mdevices_page2.h
|
||||
mdevices_page2_graph.h
|
||||
mdevices_pagectx.h
|
||||
mdevices_pagectx_p.h
|
||||
</headers>
|
||||
|
||||
|
||||
@@ -83,6 +87,9 @@
|
||||
mdevices_device.c
|
||||
mdevices_setdevice.c
|
||||
mdevices_page.c
|
||||
mdevices_page2.c
|
||||
mdevices_page2_graph.c
|
||||
mdevices_pagectx.c
|
||||
</sources>
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include "./mdevices.h"
|
||||
#include "./mdevices_p.h"
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices_index.h"
|
||||
#include "aqhome-cgi/modules/devices/mdevices_valuestable.h"
|
||||
@@ -22,6 +22,7 @@
|
||||
#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/modules/devices/mdevices_page2.h"
|
||||
#include "aqhome-cgi/modules/mdataclient.h"
|
||||
|
||||
#include <aqcgi/service/module.h>
|
||||
@@ -46,12 +47,21 @@
|
||||
#define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* global vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
GWEN_INHERIT(AQCGI_MODULE, AQH_MOD_DEVICES)
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
||||
static AQCGI_MODULE *_loadSubModule(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, const char *sModuleName);
|
||||
static int _handleRequest(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, const char *sLastPathElem);
|
||||
|
||||
@@ -103,13 +113,94 @@ static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
|
||||
|
||||
void AQH_ModDevices_Extend(AQCGI_MODULE *m, AQCGI_SERVICE *sv, const char *baseFolder)
|
||||
{
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
AQH_ModService_Extend(m, sv, baseFolder);
|
||||
|
||||
GWEN_NEW_OBJECT(AQH_MOD_DEVICES, xm);
|
||||
GWEN_INHERIT_SETDATA(AQCGI_MODULE, AQH_MOD_DEVICES, m, xm, _freeData);
|
||||
|
||||
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
|
||||
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _freeData(GWEN_UNUSED void *bp, void *p)
|
||||
{
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
xm=(AQH_MOD_DEVICES*) p;
|
||||
AQH_Value_List_free(xm->valueList);
|
||||
AQH_Device_List_free(xm->deviceList);
|
||||
GWEN_FREE_OBJECT(xm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_DEVICE_LIST *AQH_ModDevices_GetDeviceList(const AQCGI_MODULE *m)
|
||||
{
|
||||
if (m) {
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_DEVICES, m);
|
||||
if (xm) {
|
||||
return xm->deviceList;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_SetDeviceList(AQCGI_MODULE *m, AQH_DEVICE_LIST *deviceList)
|
||||
{
|
||||
if (m) {
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_DEVICES, m);
|
||||
if (xm) {
|
||||
AQH_Device_List_free(xm->deviceList);
|
||||
xm->deviceList=deviceList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_VALUE_LIST *AQH_ModDevices_GetValueList(const AQCGI_MODULE *m)
|
||||
{
|
||||
if (m) {
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_DEVICES, m);
|
||||
if (xm) {
|
||||
return xm->valueList;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_SetValueList(AQCGI_MODULE *m, AQH_VALUE_LIST *valueList)
|
||||
{
|
||||
if (m) {
|
||||
AQH_MOD_DEVICES *xm;
|
||||
|
||||
xm=GWEN_INHERIT_GETDATA(AQCGI_MODULE, AQH_MOD_DEVICES, m);
|
||||
if (xm) {
|
||||
AQH_Value_List_free(xm->valueList);
|
||||
xm->valueList=valueList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AQCGI_MODULE *_loadSubModule(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, const char *sModuleName)
|
||||
{
|
||||
return NULL;
|
||||
@@ -183,7 +274,7 @@ void _handleRqDevicePost(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *sess
|
||||
|
||||
void _handleRqPageGet(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGet, dbuf);
|
||||
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPage2Get, dbuf);
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +288,7 @@ void _handleRqPagePost(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *sessio
|
||||
|
||||
void _handleRqPageGraphGet(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQCGI_SESSION *session, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGraph, dbuf);
|
||||
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPage2Graph, dbuf);
|
||||
}
|
||||
|
||||
|
||||
@@ -583,3 +674,41 @@ AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *data
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_ReadDevicesAndValues(AQCGI_MODULE *m, AQH_DATACLIENT *dc)
|
||||
{
|
||||
AQH_DEVICE_LIST *deviceList;
|
||||
AQH_VALUE_LIST *valueList;
|
||||
|
||||
deviceList=AQH_DataClient_GetDevices(dc, NULL);
|
||||
AQH_ModDevices_SetDeviceList(m, deviceList);
|
||||
|
||||
valueList=AQH_DataClient_GetValues(dc, NULL, 0);
|
||||
AQH_ModDevices_SetValueList(m, valueList);
|
||||
if (deviceList && valueList) {
|
||||
AQH_VALUE *v;
|
||||
|
||||
v=AQH_Value_List_First(valueList);
|
||||
while(v) {
|
||||
const char *s;
|
||||
|
||||
s=AQH_Value_GetDeviceNameForSystem(v);
|
||||
if (s && *s) {
|
||||
AQH_DEVICE *device;
|
||||
|
||||
device=AQH_Device_List_GetByNameForSystem(deviceList, s);
|
||||
if (device) {
|
||||
AQH_Value_SetDevicePtr(v, device);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Device %s not found in list", s);
|
||||
}
|
||||
}
|
||||
|
||||
v=AQH_Value_List_Next(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,14 @@
|
||||
void AQH_ModDevices_Extend(AQCGI_MODULE *m, AQCGI_SERVICE *sv, const char *baseFolder);
|
||||
int AQH_ModDevices_Create(AQCGI_SERVICE *sv);
|
||||
|
||||
AQH_DEVICE_LIST *AQH_ModDevices_GetDeviceList(const AQCGI_MODULE *m);
|
||||
void AQH_ModDevices_SetDeviceList(AQCGI_MODULE *m, AQH_DEVICE_LIST *deviceList);
|
||||
|
||||
AQH_VALUE_LIST *AQH_ModDevices_GetValueList(const AQCGI_MODULE *m);
|
||||
void AQH_ModDevices_SetValueList(AQCGI_MODULE *m, AQH_VALUE_LIST *valueList);
|
||||
|
||||
void AQH_ModDevices_ReadDevicesAndValues(AQCGI_MODULE *m, AQH_DATACLIENT *dc);
|
||||
|
||||
|
||||
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
|
||||
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
|
||||
|
||||
23
apps/aqhome-cgi/modules/devices/mdevices_p.h
Normal file
23
apps/aqhome-cgi/modules/devices/mdevices_p.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/****************************************************************************
|
||||
* 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_MODULES_DEVICES_P_H
|
||||
#define AQHOME_CGI_MODULES_DEVICES_P_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices.h"
|
||||
|
||||
|
||||
struct AQH_MOD_DEVICES {
|
||||
AQH_DEVICE_LIST *deviceList;
|
||||
AQH_VALUE_LIST *valueList;
|
||||
};
|
||||
typedef struct AQH_MOD_DEVICES AQH_MOD_DEVICES;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -67,7 +67,6 @@ static void _handlePageGraph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT
|
||||
static void _genPageGraph(AQH_DATACLIENT *dc, const char *sFilename, GWEN_XMLNODE *nGraph);
|
||||
static void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd);
|
||||
static AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd);
|
||||
static uint64_t _parseTime(const char *s);
|
||||
static GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName);
|
||||
static void _mkPathForGraph(AQCGI_MODULE *m, const char *sPageId, const char *sGraphId, GWEN_BUFFER *dbuf);
|
||||
static void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf);
|
||||
@@ -286,8 +285,8 @@ void _genPageGraph(AQH_DATACLIENT *dc, const char *sFilename, GWEN_XMLNODE *nGra
|
||||
w=GWEN_XMLNode_GetIntProperty(nGraph, "width", AQH_MODDEVICES_GRAPH_WIDTH);
|
||||
h=GWEN_XMLNode_GetIntProperty(nGraph, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
|
||||
precision=GWEN_XMLNode_GetIntProperty(nGraph, "precision", 2);
|
||||
tsBegin=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "begin", "-4h"));
|
||||
tsEnd=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "end", "0"));
|
||||
tsBegin=AQH_ParseTime(GWEN_XMLNode_GetProperty(nGraph, "begin", "-4h"));
|
||||
tsEnd=AQH_ParseTime(GWEN_XMLNode_GetProperty(nGraph, "end", "0"));
|
||||
|
||||
s=GWEN_XMLNode_GetProperty(nGraph, "lowerLimit", NULL);
|
||||
if (s && *s) {
|
||||
@@ -380,69 +379,6 @@ AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurv
|
||||
|
||||
|
||||
|
||||
// TODO: move to aqhome.{c,h}
|
||||
uint64_t _parseTime(const char *s)
|
||||
{
|
||||
if (s && *s) {
|
||||
if (*s=='-') {
|
||||
uint64_t x=0;
|
||||
uint64_t now=time(NULL);
|
||||
|
||||
s++;
|
||||
while(*s && isdigit(*s)) {
|
||||
unsigned int i;
|
||||
|
||||
i=*(s++)-'0';
|
||||
x*=10;
|
||||
x+=i;
|
||||
}
|
||||
if (*s) {
|
||||
switch(*s) {
|
||||
case 0:
|
||||
case 'm': x*=60; break;
|
||||
case 'h': x*=(60*60); break;
|
||||
case 'd': x*=(60*60*24); break;
|
||||
case 'w': x*=(60*60*24*7); break;
|
||||
case 'M': x*=(60*60*24*30); break;
|
||||
case 'y': x*=(60*60*24*365); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return (now-x);
|
||||
}
|
||||
if (*s=='@') {
|
||||
int y, m, d, H, M, S;
|
||||
|
||||
if (6==sscanf(s+1, "%d/%d/%d-%d:%d:%d", &y, &m, &d, &H, &M, &S)) {
|
||||
GWEN_TIMESTAMP *ts;
|
||||
uint64_t x=0;
|
||||
|
||||
ts=GWEN_Timestamp_new(y, m, d, H, M, S);
|
||||
x=GWEN_Timestamp_toTimeT(ts);
|
||||
GWEN_Timestamp_free(ts);
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Invalid timespec [%s], expected: @YYYY/MM/DD-HH:MM:SS", s);
|
||||
return (uint64_t) (-1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned long int x;
|
||||
|
||||
if (1!=sscanf(s, "%lu", &x)) {
|
||||
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
|
||||
return (uint64_t) (-1);
|
||||
}
|
||||
return (uint64_t) x;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName)
|
||||
{
|
||||
GWEN_XMLNODE *nItem;
|
||||
|
||||
453
apps/aqhome-cgi/modules/devices/mdevices_page2.c
Normal file
453
apps/aqhome-cgi/modules/devices/mdevices_page2.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/****************************************************************************
|
||||
* 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.h"
|
||||
#include "./mdevices_page2_graph.h"
|
||||
|
||||
#include "aqhome-cgi/modules/mdataclient.h"
|
||||
#include "aqhome-cgi/modules/devices/mdevices_pagectx.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/debug.h>
|
||||
#include <gwenhywfar/timestamp.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/directory.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defs and enums
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define GBAS GWEN_Buffer_AppendString
|
||||
#define GBAA GWEN_Buffer_AppendArgs
|
||||
|
||||
|
||||
#define DOC_PAGE2 "page.html"
|
||||
|
||||
#define FOLDER_PAGES "pages2"
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void _sendPageList(AQCGI_MODULE *m, GWEN_BUFFER *dbuf);
|
||||
static GWEN_STRINGLIST *_listPageFiles(AQCGI_MODULE *m);
|
||||
static GWEN_XMLNODE *_readPage(AQCGI_MODULE *m, const char *sPageName);
|
||||
static GWEN_XMLNODE *_readPageFile(const char *sFilename);
|
||||
static void _writePage(AQCGI_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf);
|
||||
static void _writePageChildElements(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n, GWEN_BUFFER *dbuf);
|
||||
static void _writeHtmlElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf);
|
||||
static void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *n, GWEN_BUFFER *dbuf);
|
||||
static void _writePageDataSet(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf);
|
||||
static void _writePageItem(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf);
|
||||
static AQH_MD_PAGECTX *_createInitialCtx(AQCGI_MODULE *m, AQH_DATACLIENT *dc);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_ModDevices_RunPage2Get(AQCGI_MODULE *m, AQCGI_REQUEST *rq,
|
||||
GWEN_UNUSED AQCGI_SESSION *session,
|
||||
AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
GWEN_DB_NODE *dbQuery;
|
||||
const char *sPageId;
|
||||
|
||||
DBG_INFO(NULL, "RunPage2Get");
|
||||
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
||||
sPageId=dbQuery?GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL):NULL;
|
||||
if (sPageId && *sPageId) {
|
||||
GWEN_XMLNODE *fileNode;
|
||||
|
||||
fileNode=_readPage(m, sPageId);
|
||||
if (fileNode) {
|
||||
GWEN_XMLNODE *nPage;
|
||||
|
||||
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", NULL, NULL);
|
||||
if (nPage) {
|
||||
_writePage(m, dc, nPage, dbuf);
|
||||
AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120");
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No page element in file for \"%s\"", sPageId);
|
||||
}
|
||||
GWEN_XMLNode_free(fileNode);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "here");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Reading page list");
|
||||
_sendPageList(m, dbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_RunPage2Graph(AQCGI_MODULE *m, AQCGI_REQUEST *rq,
|
||||
GWEN_UNUSED AQCGI_SESSION *session,
|
||||
AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
GWEN_DB_NODE *dbQuery;
|
||||
const char *sPageId;
|
||||
const char *sGraphId;
|
||||
|
||||
DBG_INFO(NULL, "RunPageGraphGet");
|
||||
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
||||
sPageId=GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL);
|
||||
sGraphId=GWEN_DB_GetCharValue(dbQuery, "graph", 0, NULL);
|
||||
if (sPageId && *sPageId && sGraphId && *sGraphId)
|
||||
AQH_ModDevices_HandleGraph(m, rq, dc, sPageId, sGraphId, dbuf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_SetCharValue(GWEN_DB_NODE *dbDest, uint32_t flags, const char *sVar, const char *sValue, GWEN_DB_NODE *dbContext)
|
||||
{
|
||||
if (sValue && *sValue) {
|
||||
GWEN_BUFFER *tbuf;
|
||||
int rv;
|
||||
|
||||
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
rv=GWEN_DB_ReplaceVars(dbContext, sValue, tbuf);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error replacing vars for %s=[%s]", sVar, sValue);
|
||||
}
|
||||
else {
|
||||
/*DBG_ERROR(NULL, "Setting value [%s]=[%s] (%s)", sVar, GWEN_Buffer_GetStart(tbuf), sValue);*/
|
||||
GWEN_DB_SetCharValue(dbDest, flags, sVar, GWEN_Buffer_GetStart(tbuf));
|
||||
}
|
||||
GWEN_Buffer_free(tbuf);
|
||||
}
|
||||
else
|
||||
GWEN_DB_DeleteVar(dbDest, sVar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendPageList(AQCGI_MODULE *m, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
GWEN_STRINGLIST *sl;
|
||||
|
||||
GBAS(dbuf, "<h1>Page List</h1>\n");
|
||||
sl=_listPageFiles(m);
|
||||
if (sl) {
|
||||
GWEN_STRINGLISTENTRY *se;
|
||||
|
||||
GBAS(dbuf,
|
||||
"<table class=\"datatable\">\n"
|
||||
"<thead><tr><th>Page</th><th>Title</th></tr></thead>\n"
|
||||
"<tbody>\n");
|
||||
se=GWEN_StringList_FirstEntry(sl);
|
||||
while(se) {
|
||||
const char *filename;
|
||||
|
||||
filename=GWEN_StringListEntry_Data(se);
|
||||
if (filename && *filename) {
|
||||
GWEN_XMLNODE *node;
|
||||
|
||||
DBG_ERROR(NULL, "Reading file \"%s\"", filename);
|
||||
node=_readPageFile(filename);
|
||||
if (node) {
|
||||
GWEN_XMLNODE *nPage;
|
||||
|
||||
nPage=GWEN_XMLNode_FindFirstTag(node, "page", NULL, NULL);
|
||||
if (nPage) {
|
||||
const char *sId;
|
||||
const char *sTitle;
|
||||
|
||||
sId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
|
||||
sTitle=GWEN_XMLNode_GetProperty(nPage, "title", sId);
|
||||
if (sId && *sId)
|
||||
GBAA(dbuf, "<tr><td><a href=\""DOC_PAGE2"?page=%s\">%s</a></td><td>%s</td></tr>\n", sId, sId, sTitle);
|
||||
}
|
||||
GWEN_XMLNode_free(node);
|
||||
}
|
||||
}
|
||||
se=GWEN_StringListEntry_Next(se);
|
||||
}
|
||||
GBAS(dbuf, "</tbody></table>");
|
||||
GWEN_StringList_free(sl);
|
||||
}
|
||||
else {
|
||||
GBAS(dbuf, "No pages.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_STRINGLIST *_listPageFiles(AQCGI_MODULE *m)
|
||||
{
|
||||
GWEN_BUFFER *fbuf;
|
||||
AQCGI_SERVICE *sv;
|
||||
GWEN_STRINGLIST *sl;
|
||||
int rv;
|
||||
|
||||
sv=AQH_ModService_GetService(m);
|
||||
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GBAA(fbuf, "%s%s"FOLDER_PAGES, AQCGI_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S);
|
||||
sl=GWEN_StringList_new();
|
||||
rv=GWEN_Directory_GetMatchingFilesRecursively(GWEN_Buffer_GetStart(fbuf), sl, "*.xml");
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "Error reading pages (%d)", rv);
|
||||
GWEN_StringList_free(sl);
|
||||
GWEN_Buffer_free(fbuf);
|
||||
return NULL;
|
||||
}
|
||||
if (GWEN_StringList_Count(sl)<1) {
|
||||
GWEN_StringList_free(sl);
|
||||
GWEN_Buffer_free(fbuf);
|
||||
return NULL;
|
||||
}
|
||||
GWEN_Buffer_free(fbuf);
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_XMLNODE *_readPage(AQCGI_MODULE *m, const char *sPageName)
|
||||
{
|
||||
GWEN_BUFFER *fbuf;
|
||||
AQCGI_SERVICE *sv;
|
||||
GWEN_XMLNODE *fileNode;
|
||||
|
||||
sv=AQH_ModService_GetService(m);
|
||||
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GBAA(fbuf, "%s%s"FOLDER_PAGES"%s", AQCGI_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S, GWEN_DIR_SEPARATOR_S);
|
||||
AQH_ModService_EscapeToBuffer(sPageName, fbuf);
|
||||
GBAS(fbuf, ".xml");
|
||||
|
||||
fileNode=_readPageFile(GWEN_Buffer_GetStart(fbuf));
|
||||
if (fileNode==NULL) {
|
||||
DBG_INFO(NULL, "here");
|
||||
GWEN_Buffer_free(fbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GWEN_Buffer_free(fbuf);
|
||||
return fileNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_XMLNODE *_readPageFile(const char *sFilename)
|
||||
{
|
||||
GWEN_XMLNODE *fileNode;
|
||||
int rv;
|
||||
|
||||
fileNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, sFilename);
|
||||
rv=GWEN_XML_ReadFile(fileNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading \"%s\": %s (%d)", sFilename?sFilename:"<no name>", strerror(errno), errno);
|
||||
GWEN_XMLNode_free(fileNode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fileNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writePage(AQCGI_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
const char *sPageId;
|
||||
const char *s;
|
||||
AQH_MD_PAGECTX *ctx;
|
||||
|
||||
ctx=_createInitialCtx(m, dc);
|
||||
sPageId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
|
||||
|
||||
/* title */
|
||||
s=GWEN_XMLNode_GetProperty(nPage, "title", NULL);
|
||||
if (s && *s)
|
||||
GBAA(dbuf, "<h1>%s</h1>\n", s);
|
||||
|
||||
_writePageChildElements(ctx, sPageId, nPage, dbuf);
|
||||
|
||||
AQH_MDPageCtx_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writePageChildElements(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
GWEN_XMLNODE *n;
|
||||
|
||||
n=GWEN_XMLNode_GetChild(nElement);
|
||||
while(n) {
|
||||
_writePageElement(ctx, sPageId, n, dbuf);
|
||||
n=GWEN_XMLNode_Next(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writePageElement(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
if (GWEN_XMLNode_GetType(nElement)==GWEN_XMLNodeTypeTag) {
|
||||
const char *sElementName;
|
||||
|
||||
sElementName=GWEN_XMLNode_GetData(nElement);
|
||||
if (sElementName && *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, "table")==0 ||
|
||||
strcasecmp(sElementName, "tr")==0 ||
|
||||
strcasecmp(sElementName, "td")==0 ||
|
||||
strcasecmp(sElementName, "p")==0)
|
||||
_writeHtmlElement(ctx, sPageId, nElement, dbuf);
|
||||
}
|
||||
}
|
||||
else if (GWEN_XMLNode_GetType(nElement)==GWEN_XMLNodeTypeData) {
|
||||
const char *s;
|
||||
|
||||
/* copy data after stripping unnecessary blanks */
|
||||
s=GWEN_XMLNode_GetData(nElement);
|
||||
if (s && *s) {
|
||||
GWEN_BUFFER *tbuf;
|
||||
|
||||
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
GBAS(tbuf, s);
|
||||
GWEN_Text_CondenseBuffer(tbuf);
|
||||
GBAS(dbuf, GWEN_Buffer_GetStart(tbuf));
|
||||
GWEN_Buffer_free(tbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
_writePageChildElements(ctx, sPageId, nElement, dbuf);
|
||||
GBAA(dbuf, "</%s>", sElementName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writePageDataSet(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
const char *sRoom;
|
||||
const char *sVarType;
|
||||
const char *sModality;
|
||||
const char *sVarName;
|
||||
int modality;
|
||||
int varType;
|
||||
AQH_MD_PAGECTX *newCtx;
|
||||
|
||||
/* prepare new context */
|
||||
sVarType=GWEN_XMLNode_GetProperty(nElement, "vartype", NULL);
|
||||
sRoom=GWEN_XMLNode_GetProperty(nElement, "room", NULL);
|
||||
sModality=GWEN_XMLNode_GetProperty(nElement, "modality", NULL);
|
||||
sVarName=GWEN_XMLNode_GetProperty(nElement, "varname", NULL);
|
||||
modality=sModality?AQH_ValueModality_fromString(sModality):AQH_ValueModality_Unknown;
|
||||
varType=sVarType?AQH_ValueType_fromString(sVarType):AQH_ValueType_Sensor;
|
||||
newCtx=AQH_MDPageCtx_CopyWithMatchingVars(ctx, varType, sRoom, modality, sVarName);
|
||||
/* enter context */
|
||||
_writePageChildElements(newCtx, sPageId, nElement, dbuf);
|
||||
/* free new context */
|
||||
AQH_MDPageCtx_free(newCtx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_MD_PAGECTX *_createInitialCtx(AQCGI_MODULE *m, AQH_DATACLIENT *dc)
|
||||
{
|
||||
AQH_DEVICE_LIST *deviceList;
|
||||
AQH_VALUE_LIST *valueList;
|
||||
AQH_MD_PAGECTX *ctx;
|
||||
|
||||
AQH_ModDevices_ReadDevicesAndValues(m, dc);
|
||||
|
||||
deviceList=AQH_ModDevices_GetDeviceList(m);
|
||||
valueList=AQH_ModDevices_GetValueList(m);
|
||||
|
||||
ctx=AQH_MDPageCtx_new(m, dc);
|
||||
AQH_MDPageCtx_SetDevices(ctx, deviceList);
|
||||
if (valueList)
|
||||
AQH_MDPageCtx_SetValues(ctx, AQH_Value_List_dup(valueList));
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
29
apps/aqhome-cgi/modules/devices/mdevices_page2.h
Normal file
29
apps/aqhome-cgi/modules/devices/mdevices_page2.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/****************************************************************************
|
||||
* 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_H
|
||||
#define AQHOME_CGI_MDEVICES_PAGE2_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/dataclient/client.h"
|
||||
|
||||
#include <aqcgi/request.h>
|
||||
|
||||
#include <gwenhywfar/buffer.h>
|
||||
|
||||
|
||||
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_SetCharValue(GWEN_DB_NODE *dbDest, uint32_t flags, const char *sVar, const char *sValue, GWEN_DB_NODE *dbCtx);
|
||||
|
||||
|
||||
#endif
|
||||
448
apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c
Normal file
448
apps/aqhome-cgi/modules/devices/mdevices_page2_graph.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/****************************************************************************
|
||||
* 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_graph.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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defs and enums
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define GBAS GWEN_Buffer_AppendString
|
||||
#define GBAA GWEN_Buffer_AppendArgs
|
||||
|
||||
#define DEFAULT_GRAPH_WIDTH 640
|
||||
#define DEFAULT_GRAPH_HEIGHT 480
|
||||
#define DEFAULT_GRAPH_PRECISION 2
|
||||
#define DEFAULT_GRAPH_REFRESHTIME 120
|
||||
#define DEFAULT_GRAPHCONF_REFRESH 60
|
||||
|
||||
#define DOC_PAGE2_GRAPH "pgraph.html"
|
||||
|
||||
#define FOLDER_CACHE "pages2"
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _isGraphConfFileCurrent(AQH_MD_PAGECTX *ctx, const char *sPageId, const char *sGraphId, int refreshTime);
|
||||
static void _mkGraphConfigForEveryValue(AQH_MD_PAGECTX *ctx, GWEN_DB_NODE *dbGraph, GWEN_XMLNODE *n);
|
||||
static void _setupDbForDeviceAndValue(const AQH_VALUE *v, GWEN_DB_NODE *dbCtx);
|
||||
static void _mkGraphConfigCurve(AQH_MD_PAGECTX *ctx, GWEN_DB_NODE *dbGraph, GWEN_XMLNODE *nElement);
|
||||
static GWEN_DB_NODE *_dbGraphFromXml(GWEN_XMLNODE *nElement, GWEN_DB_NODE *dbCtx);
|
||||
static GWEN_DB_NODE *_dbCurveFromXml(GWEN_XMLNODE *nElement, GWEN_DB_NODE *dbCtx);
|
||||
static void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, const char *lnk, GWEN_BUFFER *dbuf);
|
||||
static void _genPageGraph(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbGraph, const char *sFilename);
|
||||
static int _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_DB_NODE *dbGraph, uint64_t tsBegin, uint64_t tsEnd);
|
||||
static void _mkPathForGraph(AQCGI_MODULE *m, const char *sPageId, const char *sGraphId, const char *ext, GWEN_BUFFER *buf);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AQH_ModDevices_PageWriteGraph(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
const char *sGraphId;
|
||||
const char *sLink;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
sLink=GWEN_XMLNode_GetProperty(nElement, "link", NULL);
|
||||
w=GWEN_XMLNode_GetIntProperty(nElement, "width", DEFAULT_GRAPH_WIDTH);
|
||||
h=GWEN_XMLNode_GetIntProperty(nElement, "height", DEFAULT_GRAPH_HEIGHT);
|
||||
sGraphId=GWEN_XMLNode_GetProperty(nElement, "id", NULL);
|
||||
if (!_isGraphConfFileCurrent(ctx, sPageId, sGraphId, DEFAULT_GRAPHCONF_REFRESH)) {
|
||||
GWEN_DB_NODE *dbGraph;
|
||||
GWEN_XMLNODE *n;
|
||||
GWEN_BUFFER *nbuf;
|
||||
int rv;
|
||||
|
||||
dbGraph=_dbGraphFromXml(nElement, AQH_MDPageCtx_GetDbVars(ctx));
|
||||
|
||||
/* write real element */
|
||||
n=GWEN_XMLNode_GetFirstTag(nElement);
|
||||
while(n) {
|
||||
const char *sName;
|
||||
|
||||
sName=GWEN_XMLNode_GetData(n);
|
||||
if (sName && *sName) {
|
||||
if (strcasecmp(sName, "ForEveryValue")==0)
|
||||
_mkGraphConfigForEveryValue(ctx, dbGraph, n);
|
||||
else if (strcasecmp(sName, "curve")==0)
|
||||
_mkGraphConfigCurve(ctx, dbGraph, n);
|
||||
else {
|
||||
/* unhandled */
|
||||
}
|
||||
}
|
||||
|
||||
n=GWEN_XMLNode_GetNextTag(n);
|
||||
}
|
||||
|
||||
/* write config file */
|
||||
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
_mkPathForGraph(AQH_MDPageCtx_GetModule(ctx), sPageId, sGraphId, "conf", nbuf);
|
||||
rv=AQH_ModService_WriteDbFileSafely(dbGraph, GWEN_Buffer_GetStart(nbuf));
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error writing graph config to \"%s\": %d", GWEN_Buffer_GetStart(nbuf), rv);
|
||||
}
|
||||
GWEN_Buffer_free(nbuf);
|
||||
GWEN_DB_Group_free(dbGraph);
|
||||
}
|
||||
|
||||
/* write graph link */
|
||||
_addGraphLink(sPageId, sGraphId, w, h, sLink, dbuf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _isGraphConfFileCurrent(AQH_MD_PAGECTX *ctx, const char *sPageId, const char *sGraphId, int refreshTime)
|
||||
{
|
||||
int rv;
|
||||
GWEN_BUFFER *nbuf;
|
||||
|
||||
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
_mkPathForGraph(AQH_MDPageCtx_GetModule(ctx), sPageId, sGraphId, "conf", nbuf);
|
||||
rv=AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(nbuf), refreshTime);
|
||||
GWEN_Buffer_free(nbuf);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _mkGraphConfigForEveryValue(AQH_MD_PAGECTX *ctx, GWEN_DB_NODE *dbGraph, GWEN_XMLNODE *nElement)
|
||||
{
|
||||
AQH_VALUE_LIST *valueList;
|
||||
|
||||
valueList=AQH_MDPageCtx_GetValues(ctx);
|
||||
if (valueList) {
|
||||
const AQH_VALUE *v;
|
||||
GWEN_DB_NODE *dbCtx;
|
||||
|
||||
dbCtx=AQH_MDPageCtx_GetDbVars(ctx);
|
||||
v=AQH_Value_List_First(valueList);
|
||||
while(v) {
|
||||
GWEN_XMLNODE *n;
|
||||
|
||||
_setupDbForDeviceAndValue(v, dbCtx);
|
||||
n=GWEN_XMLNode_GetFirstTag(nElement);
|
||||
while(n) {
|
||||
const char *sName;
|
||||
|
||||
sName=GWEN_XMLNode_GetData(n);
|
||||
if (sName && *sName) {
|
||||
if (strcasecmp(sName, "curve")==0)
|
||||
_mkGraphConfigCurve(ctx, dbGraph, n);
|
||||
else {
|
||||
/* unhandled */
|
||||
}
|
||||
}
|
||||
|
||||
n=GWEN_XMLNode_GetNextTag(n);
|
||||
}
|
||||
v=AQH_Value_List_Next(v);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No values");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setupDbForDeviceAndValue(const AQH_VALUE *v, GWEN_DB_NODE *dbCtx)
|
||||
{
|
||||
GWEN_DB_NODE *db;
|
||||
AQH_DEVICE *device;
|
||||
|
||||
db=GWEN_DB_GetGroup(dbCtx, GWEN_DB_FLAGS_OVERWRITE_GROUPS, "value");
|
||||
AQH_Value_toDb(v, db);
|
||||
|
||||
device=AQH_Value_GetDevicePtr(v);
|
||||
if (device) {
|
||||
const char *sNameForSystem;
|
||||
const char *sLocation;
|
||||
const char *sDescription;
|
||||
GWEN_BUFFER *tbuf;
|
||||
|
||||
db=GWEN_DB_GetGroup(dbCtx, GWEN_DB_FLAGS_OVERWRITE_GROUPS, "device");
|
||||
AQH_Device_toDb(device, db);
|
||||
/* generate descriptive name */
|
||||
sNameForSystem=AQH_Device_GetNameForSystem(device);
|
||||
sLocation=AQH_Device_GetLocation(device);
|
||||
sDescription=AQH_Device_GetDescription(device);
|
||||
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
if (sLocation && sDescription)
|
||||
GBAA(tbuf, "%s - %s", sDescription, sLocation);
|
||||
else if (sLocation)
|
||||
GBAA(tbuf, "%s", sLocation);
|
||||
else if (sDescription)
|
||||
GBAA(tbuf, "%s", sDescription);
|
||||
else
|
||||
GBAA(tbuf, "%s", sNameForSystem);
|
||||
|
||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "descriptiveName", GWEN_Buffer_GetStart(tbuf));
|
||||
GWEN_Buffer_free(tbuf);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No device pointer in device %s", AQH_Value_GetNameForSystem(v));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _mkGraphConfigCurve(AQH_MD_PAGECTX *ctx, GWEN_DB_NODE *dbGraph, GWEN_XMLNODE *nElement)
|
||||
{
|
||||
GWEN_DB_NODE *dbCurve;
|
||||
|
||||
dbCurve=_dbCurveFromXml(nElement, AQH_MDPageCtx_GetDbVars(ctx));
|
||||
GWEN_DB_AddGroup(dbGraph, dbCurve);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_DB_NODE *_dbGraphFromXml(GWEN_XMLNODE *nElement, GWEN_DB_NODE *dbCtx)
|
||||
{
|
||||
GWEN_DB_NODE *dbGraph;
|
||||
|
||||
dbGraph=GWEN_DB_Group_new("graph");
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "id", GWEN_XMLNode_GetProperty(nElement, "id", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "title", GWEN_XMLNode_GetProperty(nElement, "title", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "begin", GWEN_XMLNode_GetProperty(nElement, "begin", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "end", GWEN_XMLNode_GetProperty(nElement, "end", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "lowerLimit", GWEN_XMLNode_GetProperty(nElement, "lowerLimit", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbGraph, 0, "upperLimit", GWEN_XMLNode_GetProperty(nElement, "upperLimit", NULL), dbCtx);
|
||||
GWEN_DB_SetIntValue(dbGraph, 0, "width", GWEN_XMLNode_GetIntProperty(nElement, "width", DEFAULT_GRAPH_WIDTH));
|
||||
GWEN_DB_SetIntValue(dbGraph, 0, "height", GWEN_XMLNode_GetIntProperty(nElement, "height", DEFAULT_GRAPH_HEIGHT));
|
||||
GWEN_DB_SetIntValue(dbGraph, 0, "precision", GWEN_XMLNode_GetIntProperty(nElement, "precision", DEFAULT_GRAPH_PRECISION));
|
||||
GWEN_DB_SetIntValue(dbGraph, 0, "refreshTime", GWEN_XMLNode_GetIntProperty(nElement, "refreshTime", DEFAULT_GRAPH_REFRESHTIME));
|
||||
|
||||
return dbGraph;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_DB_NODE *_dbCurveFromXml(GWEN_XMLNODE *nElement, GWEN_DB_NODE *dbCtx)
|
||||
{
|
||||
GWEN_DB_NODE *dbCurve;
|
||||
|
||||
dbCurve=GWEN_DB_Group_new("curve");
|
||||
AQH_ModDevices_SetCharValue(dbCurve, 0, "title", GWEN_XMLNode_GetProperty(nElement, "title", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbCurve, 0, "value", GWEN_XMLNode_GetProperty(nElement, "value", NULL), dbCtx);
|
||||
AQH_ModDevices_SetCharValue(dbCurve, 0, "modifier", GWEN_XMLNode_GetProperty(nElement, "modifier", NULL), dbCtx);
|
||||
|
||||
return dbCurve;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, const char *lnk, GWEN_BUFFER *dbuf)
|
||||
{
|
||||
if (lnk && *lnk)
|
||||
GBAA(dbuf, "<a href=\"%s\">", lnk);
|
||||
GBAS(dbuf, "<img src=\""DOC_PAGE2_GRAPH"?page=");
|
||||
AQH_ModService_EscapeToBuffer(sPageId, dbuf);
|
||||
GBAS(dbuf, "&graph=");
|
||||
AQH_ModService_EscapeToBuffer(sGraphId, dbuf);
|
||||
GBAA(dbuf, "\" alt=\"%s\" width=\"%d\" height=\"%d\"", sGraphId, w, h);
|
||||
GBAS(dbuf, "/>");
|
||||
if (lnk && *lnk)
|
||||
GBAS(dbuf, "</a>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_ModDevices_HandleGraph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc,
|
||||
const char *sPageId, const char *sGraphId,
|
||||
GWEN_BUFFER *dbuf)
|
||||
{
|
||||
GWEN_BUFFER *nbuf;
|
||||
GWEN_DB_NODE *dbGraph;
|
||||
int rv;
|
||||
|
||||
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
_mkPathForGraph(m, sPageId, sGraphId, "conf", nbuf);
|
||||
|
||||
dbGraph=GWEN_DB_Group_new("graph");
|
||||
rv=GWEN_DB_ReadFile(dbGraph, GWEN_Buffer_GetStart(nbuf), GWEN_DB_FLAGS_DEFAULT | GWEN_PATH_FLAGS_CREATE_GROUP | GWEN_DB_FLAGS_LOCKFILE);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error reading graph config \"%s\": %d", GWEN_Buffer_GetStart(nbuf), rv);
|
||||
}
|
||||
else {
|
||||
int refreshTime;
|
||||
|
||||
refreshTime=GWEN_DB_GetIntValue(dbGraph, "refreshTime", 0, DEFAULT_GRAPH_REFRESHTIME);
|
||||
GWEN_Buffer_Reset(nbuf);
|
||||
_mkPathForGraph(m, sPageId, sGraphId, "png", nbuf);
|
||||
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(nbuf), refreshTime)) {
|
||||
DBG_INFO(NULL, "Generating graph \"%s\"", GWEN_Buffer_GetStart(nbuf));
|
||||
_genPageGraph(dc, dbGraph, GWEN_Buffer_GetStart(nbuf));
|
||||
}
|
||||
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(nbuf), "image/png", dbuf);
|
||||
}
|
||||
GWEN_DB_Group_free(dbGraph);
|
||||
GWEN_Buffer_free(nbuf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _genPageGraph(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbGraph, const char *sFilename)
|
||||
{
|
||||
const char *s;
|
||||
const char *sTitle;
|
||||
int w;
|
||||
int h;
|
||||
int precision;
|
||||
uint64_t tsBegin;
|
||||
uint64_t tsEnd;
|
||||
AQDG_GRAPH *g;
|
||||
AQDG_DRAW_CONTEXT *drawContext;
|
||||
AQDG_OBJECT *graphObject;
|
||||
uint32_t tickFlags=0;
|
||||
double upperLimit;
|
||||
double lowerLimit;
|
||||
|
||||
sTitle=GWEN_DB_GetCharValue(dbGraph, "title", 0, "untitled");
|
||||
w=GWEN_DB_GetIntValue(dbGraph, "width", 0, DEFAULT_GRAPH_WIDTH);
|
||||
h=GWEN_DB_GetIntValue(dbGraph, "height", 0, DEFAULT_GRAPH_HEIGHT);
|
||||
precision=GWEN_DB_GetIntValue(dbGraph, "precision", 0, DEFAULT_GRAPH_PRECISION);
|
||||
tsBegin=AQH_ParseTime(GWEN_DB_GetCharValue(dbGraph, "begin", 0, "-4h"));
|
||||
tsEnd=AQH_ParseTime(GWEN_DB_GetCharValue(dbGraph, "end", 0, "0"));
|
||||
|
||||
s=GWEN_DB_GetCharValue(dbGraph, "lowerLimit", 0, NULL);
|
||||
if (s && *s) {
|
||||
if (1==sscanf(s, "%lf", &lowerLimit))
|
||||
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MINY;
|
||||
else {
|
||||
DBG_ERROR(NULL, "Ignoring invalid lowerLimit (%s)", s);
|
||||
}
|
||||
}
|
||||
|
||||
s=GWEN_DB_GetCharValue(dbGraph, "upperLimit", 0, NULL);
|
||||
if (s && *s) {
|
||||
if (1==sscanf(s, "%lf", &upperLimit))
|
||||
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MAXY;
|
||||
else {
|
||||
DBG_ERROR(NULL, "Ignoring invalid upperLimit (%s)", s);
|
||||
}
|
||||
}
|
||||
|
||||
g=AQDG_TimeGraph_new(sTitle, NULL, "Value", NULL, precision);
|
||||
if (_addCurves(dc, g, dbGraph, tsBegin, tsEnd)) {
|
||||
AQDG_TimeGraph_SetupTicks(g, tickFlags, lowerLimit, upperLimit);
|
||||
|
||||
DBG_INFO(NULL, "Draw graph %s", sFilename);
|
||||
drawContext=AQDG_Draw_ContextCairo_Png_new(sFilename, w, h);
|
||||
graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
|
||||
AQDG_Object_SetWidth(graphObject, w);
|
||||
AQDG_Object_SetHeight(graphObject, h);
|
||||
|
||||
AQDG_GraphWidget_SetupDefaultPens(graphObject);
|
||||
AQDG_GraphWidget_SetupDefaultFonts(graphObject);
|
||||
|
||||
AQDG_GraphWidget_FinishWithGraph(graphObject, g);
|
||||
|
||||
AQDG_Object_free(graphObject);
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "No curves added");
|
||||
AQDG_Graph_free(g);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_DB_NODE *dbGraph, uint64_t tsBegin, uint64_t tsEnd)
|
||||
{
|
||||
GWEN_DB_NODE *dbCurve;
|
||||
int curvesAdded=0;
|
||||
|
||||
dbCurve=GWEN_DB_FindFirstGroup(dbGraph, "curve");
|
||||
if (dbCurve==NULL) {
|
||||
DBG_ERROR(NULL, "No curve found");
|
||||
}
|
||||
while(dbCurve) {
|
||||
const char *sCurveLabel;
|
||||
const char *sModifier;
|
||||
const char *sValueName;
|
||||
|
||||
sCurveLabel=GWEN_DB_GetCharValue(dbCurve, "title", 0, NULL);
|
||||
sValueName=GWEN_DB_GetCharValue(dbCurve, "value", 0, NULL);
|
||||
sModifier=GWEN_DB_GetCharValue(dbCurve, "modifier", 0, NULL);
|
||||
DBG_INFO(NULL, "Handling curve %s", sCurveLabel?sCurveLabel:"<unnamed>");
|
||||
if (sValueName && *sValueName && sModifier && *sModifier) {
|
||||
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||
|
||||
dpList=AQH_ModDevices_RequestDataPairList(dc, sValueName, tsBegin, tsEnd, 10000000);
|
||||
if (dpList) {
|
||||
|
||||
DBG_INFO(NULL, "Adding data for %s", sCurveLabel?sCurveLabel:"<no title>");
|
||||
AQDG_TimeGraph_ModifyDataAndAddCurve(g, sCurveLabel?sCurveLabel:"<no title>", sModifier, dpList);
|
||||
curvesAdded++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Incomplete curve data");
|
||||
}
|
||||
dbCurve=GWEN_DB_FindNextGroup(dbCurve, "curve");
|
||||
}
|
||||
|
||||
return curvesAdded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _mkPathForGraph(AQCGI_MODULE *m, const char *sPageId, const char *sGraphId, const char *ext, GWEN_BUFFER *buf)
|
||||
{
|
||||
AQCGI_SERVICE *sv;
|
||||
const char *s;
|
||||
int rv;
|
||||
GWEN_BUFFER *tbuf;
|
||||
|
||||
sv=AQH_ModService_GetService(m);
|
||||
|
||||
/* make sure folder exists */
|
||||
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
s=AQCGI_Service_GetCacheFolder(sv);
|
||||
GBAA(tbuf, "%s%s"FOLDER_CACHE, s, GWEN_DIR_SEPARATOR_S);
|
||||
rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(tbuf), 0);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(NULL, "Error getting path \"%s\"", GWEN_Buffer_GetStart(tbuf));
|
||||
}
|
||||
GWEN_Buffer_free(tbuf);
|
||||
|
||||
GBAA(buf, "%s%s"FOLDER_CACHE"%s%s-%s.%s", s, GWEN_DIR_SEPARATOR_S, GWEN_DIR_SEPARATOR_S, sPageId, sGraphId, ext?ext:"");
|
||||
}
|
||||
|
||||
|
||||
|
||||
25
apps/aqhome-cgi/modules/devices/mdevices_page2_graph.h
Normal file
25
apps/aqhome-cgi/modules/devices/mdevices_page2_graph.h
Normal 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_GRAPH_H
|
||||
#define AQHOME_CGI_MDEVICES_PAGE2_GRAPH_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices_page2.h"
|
||||
#include "aqhome-cgi/modules/devices/mdevices_pagectx.h"
|
||||
|
||||
|
||||
void AQH_ModDevices_PageWriteGraph(AQH_MD_PAGECTX *ctx, const char *sPageId, GWEN_XMLNODE *nElement, GWEN_BUFFER *dbuf);
|
||||
|
||||
void AQH_ModDevices_HandleGraph(AQCGI_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc,
|
||||
const char *sPageId, const char *sGraphId,
|
||||
GWEN_BUFFER *dbuf);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
29
apps/aqhome-cgi/modules/devices/mdevices_page2_p.h
Normal file
29
apps/aqhome-cgi/modules/devices/mdevices_page2_p.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/****************************************************************************
|
||||
* 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_P_H
|
||||
#define AQHOME_CGI_MDEVICES_PAGE2_P_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices_page2.h"
|
||||
|
||||
|
||||
|
||||
struct AQH_MD_PAGECTX {
|
||||
GWEN_TREE2_ELEMENT(AQH_MD_PAGECTX);
|
||||
|
||||
AQH_DEVICE_LIST *deviceList;
|
||||
AQH_VALUE_LIST *valueList;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
211
apps/aqhome-cgi/modules/devices/mdevices_pagectx.c
Normal file
211
apps/aqhome-cgi/modules/devices/mdevices_pagectx.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/****************************************************************************
|
||||
* 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_pagectx_p.h"
|
||||
|
||||
#include <gwenhywfar/misc.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* global vars
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
GWEN_TREE2_FUNCTIONS(AQH_MD_PAGECTX, AQH_MDPageCtx);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _valueMatches(const AQH_VALUE *value, int varType, const char *room, int modality, const char *vname);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* code
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
AQH_MD_PAGECTX *AQH_MDPageCtx_new(AQCGI_MODULE *module, AQH_DATACLIENT *dataClient)
|
||||
{
|
||||
AQH_MD_PAGECTX *ctx;
|
||||
|
||||
GWEN_NEW_OBJECT(AQH_MD_PAGECTX, ctx);
|
||||
if (ctx) {
|
||||
GWEN_TREE2_INIT(AQH_MD_PAGECTX, ctx, AQH_MDPageCtx);
|
||||
ctx->module=module;
|
||||
ctx->dataClient=dataClient;
|
||||
ctx->dbVars=GWEN_DB_Group_new("vars");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_MDPageCtx_free(AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
GWEN_DB_Group_free(ctx->dbVars);
|
||||
AQH_Value_List_free(ctx->valueList);
|
||||
GWEN_TREE2_FINI(AQH_MD_PAGECTX, ctx, AQH_MDPageCtx);
|
||||
GWEN_FREE_OBJECT(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_MD_PAGECTX *AQH_MDPageCtx_CopyWithMatchingVars(const AQH_MD_PAGECTX *octx,
|
||||
int varType, const char *room, int modality, const char *vname)
|
||||
{
|
||||
AQH_MD_PAGECTX *ctx=NULL;
|
||||
|
||||
if (octx) {
|
||||
ctx=AQH_MDPageCtx_new(octx->module, octx->dataClient);
|
||||
if (ctx) {
|
||||
/* copy dbVars */
|
||||
GWEN_DB_Group_free(ctx->dbVars);
|
||||
ctx->dbVars=GWEN_DB_Group_dup(octx->dbVars);
|
||||
/* copy device list */
|
||||
ctx->deviceList=octx->deviceList;
|
||||
/* copy matching values from value list */
|
||||
if (octx->valueList) {
|
||||
AQH_VALUE_LIST *valueList;
|
||||
const AQH_VALUE *vSrc;
|
||||
|
||||
DBG_ERROR(NULL, "Copying values");
|
||||
valueList=AQH_Value_List_new();
|
||||
vSrc=AQH_Value_List_First(octx->valueList);
|
||||
while(vSrc) {
|
||||
if (_valueMatches(vSrc, varType, room, modality, vname)) {
|
||||
AQH_VALUE *vDest;
|
||||
|
||||
DBG_ERROR(NULL, "+ %s", AQH_Value_GetNameForSystem(vSrc));
|
||||
vDest=AQH_Value_dup(vSrc);
|
||||
AQH_Value_List_Add(vDest, valueList);
|
||||
}
|
||||
vSrc=AQH_Value_List_Next(vSrc);
|
||||
}
|
||||
|
||||
AQH_Value_List_free(ctx->valueList);
|
||||
if (AQH_Value_List_GetCount(valueList))
|
||||
ctx->valueList=valueList;
|
||||
else
|
||||
AQH_Value_List_free(valueList);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _valueMatches(const AQH_VALUE *value, int varType, const char *room, int modality, const char *vname)
|
||||
{
|
||||
int matches=1;
|
||||
|
||||
/* fast compares first */
|
||||
if ((varType!=AQH_ValueType_Unknown) && (AQH_Value_GetValueType(value)!=varType))
|
||||
matches=0;
|
||||
if (matches && modality && modality!=AQH_Value_GetModality(value))
|
||||
matches=0;
|
||||
/* then slower compares */
|
||||
if (matches && vname) {
|
||||
const char *sName;
|
||||
|
||||
sName=AQH_Value_GetNameForSystem(value);
|
||||
if (GWEN_Text_ComparePattern(sName, vname, 0)==-1)
|
||||
matches=0;
|
||||
}
|
||||
|
||||
if (matches && room) {
|
||||
const AQH_DEVICE *device;
|
||||
|
||||
device=AQH_Value_GetDevicePtr(value);
|
||||
if (device) {
|
||||
const char *s;
|
||||
|
||||
s=AQH_Device_GetRoomName(device);
|
||||
if (s && strcasecmp(s, room)!=0)
|
||||
matches=0;
|
||||
}
|
||||
}
|
||||
|
||||
return matches?1:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const AQH_DEVICE_LIST *AQH_MDPageCtx_GetDevices(const AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
return ctx?ctx->deviceList:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_MDPageCtx_SetDevices(AQH_MD_PAGECTX *ctx, const AQH_DEVICE_LIST *dList)
|
||||
{
|
||||
if (ctx)
|
||||
ctx->deviceList=dList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_VALUE_LIST *AQH_MDPageCtx_GetValues(const AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
return ctx?ctx->valueList:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_MDPageCtx_SetValues(AQH_MD_PAGECTX *ctx, AQH_VALUE_LIST *vList)
|
||||
{
|
||||
if (ctx)
|
||||
ctx->valueList=vList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_DB_NODE *AQH_MDPageCtx_GetDbVars(const AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
return ctx?ctx->dbVars:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQCGI_MODULE *AQH_MDPageCtx_GetModule(const AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
return ctx?ctx->module:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_DATACLIENT *AQH_MDPageCtx_GetDataClient(const AQH_MD_PAGECTX *ctx)
|
||||
{
|
||||
return ctx?ctx->dataClient:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
53
apps/aqhome-cgi/modules/devices/mdevices_pagectx.h
Normal file
53
apps/aqhome-cgi/modules/devices/mdevices_pagectx.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/****************************************************************************
|
||||
* 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_PAGECTX_H
|
||||
#define AQHOME_CGI_MDEVICES_PAGECTX_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices.h"
|
||||
|
||||
#include "aqhome/aqhome.h"
|
||||
#include "aqhome/dataclient/client.h"
|
||||
#include "aqhome/data/device.h"
|
||||
#include "aqhome/data/value.h"
|
||||
|
||||
#include <aqcgi/request.h>
|
||||
|
||||
#include <gwenhywfar/db.h>
|
||||
#include <gwenhywfar/buffer.h>
|
||||
#include <gwenhywfar/tree2.h>
|
||||
|
||||
|
||||
|
||||
typedef struct AQH_MD_PAGECTX AQH_MD_PAGECTX;
|
||||
GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_MD_PAGECTX, AQH_MDPageCtx, AQHOME_API);
|
||||
|
||||
|
||||
|
||||
AQH_MD_PAGECTX *AQH_MDPageCtx_new(AQCGI_MODULE *module, AQH_DATACLIENT *dataClient);
|
||||
void AQH_MDPageCtx_free(AQH_MD_PAGECTX *ctx);
|
||||
|
||||
AQH_MD_PAGECTX *AQH_MDPageCtx_CopyWithMatchingVars(const AQH_MD_PAGECTX *octx,
|
||||
int varType, const char *room, int modality, const char *vname);
|
||||
|
||||
AQCGI_MODULE *AQH_MDPageCtx_GetModule(const AQH_MD_PAGECTX *ctx);
|
||||
AQH_DATACLIENT *AQH_MDPageCtx_GetDataClient(const AQH_MD_PAGECTX *ctx);
|
||||
|
||||
const AQH_DEVICE_LIST *AQH_MDPageCtx_GetDevices(const AQH_MD_PAGECTX *ctx);
|
||||
void AQH_MDPageCtx_SetDevices(AQH_MD_PAGECTX *ctx, const AQH_DEVICE_LIST *dList);
|
||||
|
||||
AQH_VALUE_LIST *AQH_MDPageCtx_GetValues(const AQH_MD_PAGECTX *ctx);
|
||||
void AQH_MDPageCtx_SetValues(AQH_MD_PAGECTX *ctx, AQH_VALUE_LIST *vList);
|
||||
|
||||
GWEN_DB_NODE *AQH_MDPageCtx_GetDbVars(const AQH_MD_PAGECTX *ctx);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
33
apps/aqhome-cgi/modules/devices/mdevices_pagectx_p.h
Normal file
33
apps/aqhome-cgi/modules/devices/mdevices_pagectx_p.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
* 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_PAGECTX_P_H
|
||||
#define AQHOME_CGI_MDEVICES_PAGECTX_P_H
|
||||
|
||||
|
||||
#include "aqhome-cgi/modules/devices/mdevices_pagectx.h"
|
||||
|
||||
|
||||
|
||||
struct AQH_MD_PAGECTX {
|
||||
GWEN_TREE2_ELEMENT(AQH_MD_PAGECTX);
|
||||
|
||||
AQCGI_MODULE *module;
|
||||
AQH_DATACLIENT *dataClient;
|
||||
|
||||
const AQH_DEVICE_LIST *deviceList;
|
||||
AQH_VALUE_LIST *valueList;
|
||||
|
||||
GWEN_DB_NODE *dbVars;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -18,9 +18,13 @@
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/i18n.h>
|
||||
#include <gwenhywfar/stringlist.h>
|
||||
#include <gwenhywfar/timestamp.h>
|
||||
#include <gwenhywfar/directory.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -308,6 +312,67 @@ const char *AQH_ValueModality_toString(int i)
|
||||
|
||||
|
||||
|
||||
uint64_t AQH_ParseTime(const char *s)
|
||||
{
|
||||
if (s && *s) {
|
||||
if (*s=='-') {
|
||||
uint64_t x=0;
|
||||
uint64_t now=time(NULL);
|
||||
|
||||
s++;
|
||||
while(*s && isdigit(*s)) {
|
||||
unsigned int i;
|
||||
|
||||
i=*(s++)-'0';
|
||||
x*=10;
|
||||
x+=i;
|
||||
}
|
||||
if (*s) {
|
||||
switch(*s) {
|
||||
case 0:
|
||||
case 'm': x*=60; break;
|
||||
case 'h': x*=(60*60); break;
|
||||
case 'd': x*=(60*60*24); break;
|
||||
case 'w': x*=(60*60*24*7); break;
|
||||
case 'M': x*=(60*60*24*30); break;
|
||||
case 'y': x*=(60*60*24*365); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return (now-x);
|
||||
}
|
||||
if (*s=='@') {
|
||||
int y, m, d, H, M, S;
|
||||
|
||||
if (6==sscanf(s+1, "%d/%d/%d-%d:%d:%d", &y, &m, &d, &H, &M, &S)) {
|
||||
GWEN_TIMESTAMP *ts;
|
||||
uint64_t x=0;
|
||||
|
||||
ts=GWEN_Timestamp_new(y, m, d, H, M, S);
|
||||
x=GWEN_Timestamp_toTimeT(ts);
|
||||
GWEN_Timestamp_free(ts);
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(NULL, "Invalid timespec [%s], expected: @YYYY/MM/DD-HH:MM:SS", s);
|
||||
return (uint64_t) (-1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned long int x;
|
||||
|
||||
if (1!=sscanf(s, "%lu", &x)) {
|
||||
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
|
||||
return (uint64_t) (-1);
|
||||
}
|
||||
return (uint64_t) x;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,8 @@ AQHOME_API const char *AQH_ValueModality_toString(int i);
|
||||
AQHOME_API int AQH_ReadDataFromString(int dataType, const char *s, uint16_t *pDataVal, uint16_t *pDataDenom);
|
||||
AQHOME_API int AQH_ReadDataFromDouble(int dataType, double d, uint16_t *pDataVal, uint16_t *pDataDenom);
|
||||
|
||||
AQHOME_API uint64_t AQH_ParseTime(const char *s);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
<headers>
|
||||
<header type="sys" loc="pre">aqhome/api.h</header>
|
||||
<header type="sys" loc="post">aqhome/data/device.h</header>
|
||||
</headers>
|
||||
|
||||
<inlines>
|
||||
@@ -123,6 +124,18 @@
|
||||
</member>
|
||||
|
||||
|
||||
<member name="devicePtr" type="AQH_DEVICE" >
|
||||
<access>public</access>
|
||||
<flags>volatile</flags>
|
||||
<dupflags>assign</dupflags>
|
||||
<copyflags>assign</copyflags>
|
||||
<getflags>none</getflags>
|
||||
<setflags>none</setflags>
|
||||
<default>NULL</default>
|
||||
<preset>NULL</preset>
|
||||
</member>
|
||||
|
||||
|
||||
</members>
|
||||
|
||||
</type>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<gwbuild>
|
||||
|
||||
<subdirs>
|
||||
cgi-examples
|
||||
mcu-examples
|
||||
</subdirs>
|
||||
|
||||
|
||||
20
doc/cgi-examples/0BUILD
Normal file
20
doc/cgi-examples/0BUILD
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml?>
|
||||
|
||||
<gwbuild>
|
||||
|
||||
<data dist="true">
|
||||
buero_climate.xml
|
||||
buero_hum.xml
|
||||
buero_light.xml
|
||||
buero_temp.xml
|
||||
|
||||
stube_climate.xml
|
||||
stube_hum.xml
|
||||
stube_light.xml
|
||||
stube_temp.xml
|
||||
</data>
|
||||
|
||||
<subdirs>
|
||||
</subdirs>
|
||||
|
||||
</gwbuild>
|
||||
51
doc/cgi-examples/buero_climate.xml
Normal file
51
doc/cgi-examples/buero_climate.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<page id="buero_climate" title="Klima im Buero">
|
||||
<dataset room="Buero">
|
||||
<table>
|
||||
<tr>
|
||||
<dataset modality="Temperature">
|
||||
<td>
|
||||
<item>
|
||||
<graph id="temp4h" title="Temperature (last 4h)" link="page.html?page=buero_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=buero_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=buero_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>
|
||||
73
doc/cgi-examples/buero_hum.xml
Normal file
73
doc/cgi-examples/buero_hum.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<page id="buero_hum" title="Luftfeuchtigkeit im Buero">
|
||||
<dataset room="Buero" 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>
|
||||
73
doc/cgi-examples/buero_light.xml
Normal file
73
doc/cgi-examples/buero_light.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<page id="buero_light" title="Helligkeit im Buero">
|
||||
<dataset room="Buero" modality="Light">
|
||||
<table>
|
||||
<tr>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</page>
|
||||
73
doc/cgi-examples/buero_temp.xml
Normal file
73
doc/cgi-examples/buero_temp.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<page id="buero_temp" title="Temperaturen im Buero">
|
||||
<dataset room="Buero" 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>
|
||||
51
doc/cgi-examples/stube_climate.xml
Normal file
51
doc/cgi-examples/stube_climate.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<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>
|
||||
73
doc/cgi-examples/stube_hum.xml
Normal file
73
doc/cgi-examples/stube_hum.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<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>
|
||||
73
doc/cgi-examples/stube_light.xml
Normal file
73
doc/cgi-examples/stube_light.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<page id="stube_light" title="Helligkeit im Wohnzimmer">
|
||||
<dataset room="Stube" modality="Light">
|
||||
<table>
|
||||
<tr>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</page>
|
||||
73
doc/cgi-examples/stube_temp.xml
Normal file
73
doc/cgi-examples/stube_temp.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user