aqhome-cgi: more code sharing, adding page handling
allows to define your own pages with graphs, sensors and actors.
This commit is contained in:
@@ -17,6 +17,14 @@
|
|||||||
|
|
||||||
#include <gwenhywfar/debug.h>
|
#include <gwenhywfar/debug.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -680,5 +688,133 @@ AQH_USER_LIST *AQH_ModService_LoadRawUsers(AQH_MODULE *m)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
while (*src) {
|
||||||
|
unsigned char x;
|
||||||
|
|
||||||
|
x=(unsigned char)*src;
|
||||||
|
if (!(
|
||||||
|
(x>='A' && x<='Z') ||
|
||||||
|
(x>='a' && x<='z') ||
|
||||||
|
(x>='0' && x<='9') ||
|
||||||
|
NULL!=strchr("-_", x)
|
||||||
|
)) {
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
GWEN_Buffer_AppendByte(buf, '%');
|
||||||
|
c=(((unsigned char)(*src))>>4)&0xf;
|
||||||
|
if (c>9)
|
||||||
|
c+=7;
|
||||||
|
c+='0';
|
||||||
|
GWEN_Buffer_AppendByte(buf, c);
|
||||||
|
c=((unsigned char)(*src))&0xf;
|
||||||
|
if (c>9)
|
||||||
|
c+=7;
|
||||||
|
c+='0';
|
||||||
|
GWEN_Buffer_AppendByte(buf, c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
GWEN_Buffer_AppendByte(buf, *src);
|
||||||
|
|
||||||
|
src++;
|
||||||
|
} /* while */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
while (*src) {
|
||||||
|
int charHandled=0;
|
||||||
|
|
||||||
|
if (*src=='%') {
|
||||||
|
if (strlen(src)>2) {
|
||||||
|
unsigned char d1, d2;
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
if (isxdigit((int)src[1]) && isxdigit((int)src[2])) {
|
||||||
|
/* skip '%' */
|
||||||
|
src++;
|
||||||
|
/* read first digit */
|
||||||
|
d1=(unsigned char)(toupper(*src));
|
||||||
|
|
||||||
|
/* get second digit */
|
||||||
|
src++;
|
||||||
|
d2=(unsigned char)(toupper(*src));
|
||||||
|
/* compute character */
|
||||||
|
d1-='0';
|
||||||
|
if (d1>9)
|
||||||
|
d1-=7;
|
||||||
|
c=(d1<<4)&0xf0;
|
||||||
|
d2-='0';
|
||||||
|
if (d2>9)
|
||||||
|
d2-=7;
|
||||||
|
c+=(d2&0xf);
|
||||||
|
/* store character */
|
||||||
|
GWEN_Buffer_AppendByte(buf, (char)c);
|
||||||
|
charHandled=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!charHandled)
|
||||||
|
GWEN_Buffer_AppendByte(buf, *src);
|
||||||
|
src++;
|
||||||
|
} /* while */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
time_t t1;
|
||||||
|
|
||||||
|
if (lstat(sPath, &sb)==-1) {
|
||||||
|
DBG_ERROR(NULL, "Error on lstat(%s): %s (%d)", sPath, strerror(errno), errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
t1=time(0);
|
||||||
|
if ((t1-sb.st_mtime)<(time_t) seconds) {
|
||||||
|
DBG_DEBUG(NULL, "File %s is current", sPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *ibuf;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ibuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
||||||
|
/* read file */
|
||||||
|
rv=GWEN_SyncIo_Helper_ReadFile(sFilename, ibuf);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_ERROR(NULL, "Error reading \"%s\" (%d)", sFilename, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GWEN_Buffer_Reset(dbuf);
|
||||||
|
GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf));
|
||||||
|
|
||||||
|
if (sMimeType && *sMimeType) {
|
||||||
|
GWEN_BUFFER *tbuf;
|
||||||
|
|
||||||
|
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAA(tbuf, "Content-type: %s", sMimeType);
|
||||||
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
|
||||||
|
GWEN_Buffer_free(tbuf);
|
||||||
|
}
|
||||||
|
AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE);
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(ibuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODU
|
|||||||
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn);
|
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn);
|
||||||
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn);
|
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn);
|
||||||
|
|
||||||
|
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf);
|
||||||
|
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf);
|
||||||
|
|
||||||
|
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds);
|
||||||
|
|
||||||
|
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
mdevices_vgraph.h
|
mdevices_vgraph.h
|
||||||
mdevices_device.h
|
mdevices_device.h
|
||||||
mdevices_setdevice.h
|
mdevices_setdevice.h
|
||||||
|
mdevices_page.h
|
||||||
</headers>
|
</headers>
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@
|
|||||||
mdevices_vgraph.c
|
mdevices_vgraph.c
|
||||||
mdevices_device.c
|
mdevices_device.c
|
||||||
mdevices_setdevice.c
|
mdevices_setdevice.c
|
||||||
|
mdevices_page.c
|
||||||
</sources>
|
</sources>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "aqhome-cgi/modules/devices/mdevices_vgraph.h"
|
#include "aqhome-cgi/modules/devices/mdevices_vgraph.h"
|
||||||
#include "aqhome-cgi/modules/devices/mdevices_device.h"
|
#include "aqhome-cgi/modules/devices/mdevices_device.h"
|
||||||
#include "aqhome-cgi/modules/devices/mdevices_setdevice.h"
|
#include "aqhome-cgi/modules/devices/mdevices_setdevice.h"
|
||||||
|
#include "aqhome-cgi/modules/devices/mdevices_page.h"
|
||||||
#include "aqhome-cgi/service/module.h"
|
#include "aqhome-cgi/service/module.h"
|
||||||
#include "aqhome-cgi/modules/mdataclient.h"
|
#include "aqhome-cgi/modules/mdataclient.h"
|
||||||
|
|
||||||
@@ -61,6 +62,11 @@ static void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *
|
|||||||
static void _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
static void _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
static void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
static void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
static void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
static void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
|
||||||
|
|
||||||
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
||||||
static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
||||||
@@ -81,6 +87,9 @@ static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
|
|||||||
{"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet},
|
{"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet},
|
||||||
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
|
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
|
||||||
{"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet},
|
{"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet},
|
||||||
|
{"page.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGet},
|
||||||
|
{"page.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqPagePost},
|
||||||
|
{"pgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGraphGet},
|
||||||
{NULL, 0, 0, NULL}
|
{NULL, 0, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,6 +180,26 @@ void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGet, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPagePost, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGraph, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -227,6 +256,44 @@ uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w)
|
||||||
|
{
|
||||||
|
uint32_t colorOut;
|
||||||
|
|
||||||
|
colorOut=((r & 0xff)<<16) | ((g & 0xff)<<24) | (b &0xff) | ((w & 0xff)<<8);
|
||||||
|
return colorOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetR(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color & 0xff0000)>>16;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetG(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color & 0xff000000)>>24;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetB(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetW(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color & 0xff00)>>8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
|
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
|
||||||
{
|
{
|
||||||
const char *sValueName;
|
const char *sValueName;
|
||||||
@@ -392,4 +459,80 @@ AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue)
|
||||||
|
{
|
||||||
|
const char *sValueSystemName;
|
||||||
|
uint64_t dataPoints[2];
|
||||||
|
uint64_t recvdNum;
|
||||||
|
// uint64_t timestamp;
|
||||||
|
union {double f; uint64_t i;} u;
|
||||||
|
int intVal;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
|
||||||
|
if (recvdNum>0) {
|
||||||
|
// timestamp=dataPoints[0];
|
||||||
|
u.i=dataPoints[1];
|
||||||
|
intVal=(int) u.f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName);
|
||||||
|
intVal=defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
|
||||||
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
|
||||||
|
{
|
||||||
|
uint64_t *dataPoints;
|
||||||
|
uint64_t recvdNum;
|
||||||
|
|
||||||
|
dataPoints=malloc(num*sizeof(uint64_t)*2);
|
||||||
|
|
||||||
|
recvdNum=AQH_DataClient_GetPeriodData(dc, systemValueName, dataPoints, num, tsBegin, tsEnd);
|
||||||
|
if (recvdNum>0) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
|
||||||
|
dpList=_createDataPairListFromDataPoints(dataPoints, recvdNum);
|
||||||
|
free(dataPoints);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "No data received for %s", systemValueName);
|
||||||
|
free(dataPoints);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues)
|
||||||
|
{
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
uint64_t i;
|
||||||
|
|
||||||
|
DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues);
|
||||||
|
dpList=AQDG_Graph_DataPair_List_new();
|
||||||
|
for(i=0; i<numValues; i++) {
|
||||||
|
AQDG_GRAPH_DATAPAIR *dp;
|
||||||
|
double timestamp;
|
||||||
|
union {double f; uint64_t i;} u;
|
||||||
|
|
||||||
|
timestamp=(double)(*(dataPoints++));
|
||||||
|
u.i=*(dataPoints++);
|
||||||
|
dp=AQDG_Graph_DataPair_new();
|
||||||
|
AQDG_Graph_DataPair_SetValueX(dp, timestamp);
|
||||||
|
AQDG_Graph_DataPair_SetValueY(dp, u.f);
|
||||||
|
AQDG_Graph_DataPair_List_Add(dp, dpList);
|
||||||
|
}
|
||||||
|
AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <aqcgi/request.h>
|
#include <aqcgi/request.h>
|
||||||
|
|
||||||
|
#include <aqdiagram/graph/datapair.h>
|
||||||
|
|
||||||
#include <gwenhywfar/buffer.h>
|
#include <gwenhywfar/buffer.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +46,22 @@ int AQH_ModDevices_Create(AQH_SERVICE *sv);
|
|||||||
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
|
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
|
||||||
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
|
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
|
||||||
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn);
|
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn);
|
||||||
|
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w);
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetR(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetG(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetB(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetW(uint32_t color);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName);
|
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName);
|
||||||
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName);
|
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName);
|
||||||
|
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue);
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
|
||||||
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
868
apps/aqhome-cgi/modules/devices/mdevices_page.c
Normal file
868
apps/aqhome-cgi/modules/devices/mdevices_page.c
Normal file
@@ -0,0 +1,868 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "./mdevices_page.h"
|
||||||
|
|
||||||
|
#include "aqhome-cgi/service/module.h"
|
||||||
|
#include "aqhome-cgi/modules/mdataclient.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 <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
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MY_LAYOUT_NONE=0,
|
||||||
|
MY_LAYOUT_HORIZONTAL,
|
||||||
|
MY_LAYOUT_VERTICAL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* forward declarations
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
static void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor);
|
||||||
|
static void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf);
|
||||||
|
static void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, 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(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *dbuf);
|
||||||
|
static void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, 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 _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);
|
||||||
|
|
||||||
|
static GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName);
|
||||||
|
static GWEN_XMLNODE *_readPageFile(const char *sFilename);
|
||||||
|
static int _layoutFromString(const char *s);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* code
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbQuery;
|
||||||
|
const char *sPageId;
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "RunPageGet");
|
||||||
|
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
||||||
|
sPageId=GWEN_DB_GetCharValue(dbQuery, "page", 0, 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_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) {
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
fileNode=_readPage(m, sPageId);
|
||||||
|
if (fileNode) {
|
||||||
|
GWEN_XMLNODE *nPage;
|
||||||
|
GWEN_XMLNODE *nGraph;
|
||||||
|
|
||||||
|
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId);
|
||||||
|
nGraph=_getSubItemNode(nPage, sGraphId, "graph");
|
||||||
|
if (nPage && nGraph)
|
||||||
|
_handlePageGraph(m, rq, dc, sGraphId, nGraph, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Graph %s/%s not found", sPageId, sGraphId);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_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=_getSubItemNode(nPage, sActorId, "actor");
|
||||||
|
if (nPage && nActor) {
|
||||||
|
_handlePageActor(rq, dc, sActorId, nActor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Actor %s/%s not found", sPageId, sActorId);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sPageId && *sPageId) {
|
||||||
|
GWEN_BUFFER *pbuf;
|
||||||
|
|
||||||
|
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAS(pbuf, "Location: page.html?page=");
|
||||||
|
GWEN_Text_EscapeToBuffer(sPageId, pbuf);
|
||||||
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
||||||
|
GWEN_Buffer_free(pbuf);
|
||||||
|
}
|
||||||
|
AQCGI_Request_SetResponseCode(rq, 303);
|
||||||
|
AQCGI_Request_SetResponseText(rq, "See other");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbPost;
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
|
||||||
|
dbPost=AQCGI_Request_GetDbPostBody(rq);
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(nActor, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(nActor, "value", NULL);
|
||||||
|
|
||||||
|
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQH_VALUE *value;
|
||||||
|
|
||||||
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
|
if (value) {
|
||||||
|
const char *sSystemValueName;
|
||||||
|
|
||||||
|
sSystemValueName=AQH_Value_GetNameForSystem(value);
|
||||||
|
if (sSystemValueName) {
|
||||||
|
const char *sData;
|
||||||
|
|
||||||
|
sData=GWEN_DB_GetCharValue(dbPost, sActorId, 0, NULL);
|
||||||
|
DBG_INFO(NULL, "Setting value %s to %s", sSystemValueName, 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:
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *fbuf;
|
||||||
|
int refreshTime;
|
||||||
|
|
||||||
|
refreshTime=GWEN_XMLNode_GetIntProperty(nGraph, "refreshTime", 120);
|
||||||
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
_mkPathForGraph(m, sGraphId, fbuf);
|
||||||
|
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), refreshTime)) {
|
||||||
|
_genPageGraph(dc, sGraphId, GWEN_Buffer_GetStart(fbuf), nGraph);
|
||||||
|
}
|
||||||
|
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, const char *sFilename, GWEN_XMLNODE *nGraph)
|
||||||
|
{
|
||||||
|
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_XMLNode_GetProperty(nGraph, "title", "untitled");
|
||||||
|
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"));
|
||||||
|
|
||||||
|
s=GWEN_XMLNode_GetProperty(nGraph, "lowerLimit", 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_XMLNode_GetProperty(nGraph, "upperLimit", 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);
|
||||||
|
_addCurves(dc, g, nGraph, tsBegin, tsEnd);
|
||||||
|
AQDG_TimeGraph_SetupTicks(g, tickFlags, lowerLimit, upperLimit);
|
||||||
|
|
||||||
|
DBG_DEBUG(NULL, "Draw graph for %s", sGraphId);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *nCurve;
|
||||||
|
|
||||||
|
nCurve=GWEN_XMLNode_FindFirstTag(nGraph, "curve", NULL, NULL);
|
||||||
|
while(nCurve) {
|
||||||
|
const char *sModifier;
|
||||||
|
|
||||||
|
sModifier=GWEN_XMLNode_GetProperty(nCurve, "modifier", NULL);
|
||||||
|
if (sModifier && *sModifier) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
|
||||||
|
dpList=_readCurveData(dc, nCurve, tsBegin, tsEnd);
|
||||||
|
if (dpList) {
|
||||||
|
const char *sCurveLabel;
|
||||||
|
|
||||||
|
sCurveLabel=GWEN_XMLNode_GetProperty(nCurve, "title", NULL);
|
||||||
|
DBG_DEBUG(NULL, "Adding data for %s", sCurveLabel?sCurveLabel:"<no title>");
|
||||||
|
AQDG_TimeGraph_ModifyDataAndAddCurve(g, sCurveLabel?sCurveLabel:"<no title>", sModifier, dpList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nCurve=GWEN_XMLNode_FindNextTag(nCurve, "curve", NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd)
|
||||||
|
{
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(nCurve, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(nCurve, "value", NULL);
|
||||||
|
|
||||||
|
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
GWEN_BUFFER *vbuf;
|
||||||
|
|
||||||
|
vbuf=GWEN_Buffer_new(0, 64, 0, 1);
|
||||||
|
GBAA(vbuf, "%s/%s", sDeviceName, sValueName);
|
||||||
|
dpList=AQH_ModDevices_RequestDataPairList(dc, GWEN_Buffer_GetStart(vbuf), tsBegin, tsEnd, 100000);
|
||||||
|
if (dpList) {
|
||||||
|
GWEN_Buffer_free(vbuf);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(vbuf);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
|
||||||
|
while(nItem) {
|
||||||
|
GWEN_XMLNODE *nGraph;
|
||||||
|
|
||||||
|
nGraph=GWEN_XMLNode_FindFirstTag(nItem, sElementName, "id", sId);
|
||||||
|
if (nGraph)
|
||||||
|
return nGraph;
|
||||||
|
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _mkPathForGraph(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
AQH_SERVICE *sv;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
sv=AQH_ModService_GetService(m);
|
||||||
|
s=AQH_Service_GetCacheFolder(sv);
|
||||||
|
GBAA(buf, "%s%s%s", s, GWEN_DIR_SEPARATOR_S, sGraphId);
|
||||||
|
AQH_ModService_EscapeToBuffer(s, buf);
|
||||||
|
GBAS(buf, ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sPageId;
|
||||||
|
GWEN_XMLNODE *nItem;
|
||||||
|
int layout;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nPage, "layout", "none"));
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<table class=\"pageTable\">\n");
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
|
||||||
|
while(nItem) {
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td>");
|
||||||
|
_writeItem(m, dc, sPageId, nItem, dbuf);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>");
|
||||||
|
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
|
||||||
|
} /* while */
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</table> <!-- pageTable -->\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *n;
|
||||||
|
uint32_t perms;
|
||||||
|
int layout;
|
||||||
|
|
||||||
|
perms=AQH_ModService_GetUserPerms(m);
|
||||||
|
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nItem, "layout", "none"));
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<table class=\"itemTable\">\n");
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
n=GWEN_XMLNode_GetFirstTag(nItem);
|
||||||
|
while(n) {
|
||||||
|
const char *sName;
|
||||||
|
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
sName=GWEN_XMLNode_GetData(n);
|
||||||
|
if (sName && *sName) {
|
||||||
|
if (strcasecmp(sName, "actor")==0) {
|
||||||
|
if (perms && AQH_MODDEVICES_PERMS_VALUEWRITE)
|
||||||
|
_writeActor(dc, sPageId, n, layout, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "No permissions to write values");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sName, "graph")==0)
|
||||||
|
_writeGraph(sPageId, n, layout, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Ignoring element \"%s\"", sName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
n=GWEN_XMLNode_GetNextTag(n);
|
||||||
|
}
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</table> <!-- itemTable -->\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sActorId;
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
const char *sLabel;
|
||||||
|
|
||||||
|
sActorId=GWEN_XMLNode_GetProperty(n, "id", NULL);
|
||||||
|
sLabel=GWEN_XMLNode_GetProperty(n, "label", NULL);
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(n, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(n, "value", NULL);
|
||||||
|
if (sActorId && *sActorId && sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQH_VALUE *value;
|
||||||
|
|
||||||
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
|
if (value) {
|
||||||
|
int lastData;
|
||||||
|
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
lastData=AQH_ModDevices_ValueGetLastDataAsInt(dc, value, 0);
|
||||||
|
GBAS(dbuf,"<form action=\"page.html\" 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);
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "Adding actor");
|
||||||
|
if (sLabel && *sLabel)
|
||||||
|
GBAA(dbuf,"<label for=\"%s\">%s</label>", sActorId, sLabel);
|
||||||
|
if (layout!=MY_LAYOUT_NONE) {
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
}
|
||||||
|
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: GBAA(dbuf, "%d", lastData); break;
|
||||||
|
} /* switch */
|
||||||
|
if (layout!=MY_LAYOUT_NONE) {
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
}
|
||||||
|
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
|
||||||
|
GBAS(dbuf, "</form>\n\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sGraphId;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
|
||||||
|
w=GWEN_XMLNode_GetIntProperty(n, "width", AQH_MODDEVICES_GRAPH_WIDTH);
|
||||||
|
h=GWEN_XMLNode_GetIntProperty(n, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
|
||||||
|
sGraphId=GWEN_XMLNode_GetProperty(n, "id", NULL);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td colspan=\"3\">\n");
|
||||||
|
if (sGraphId && *sGraphId)
|
||||||
|
_addGraphLink(sPageId, sGraphId, w, h, dbuf);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GBAS(dbuf, "<img src=\"pgraph.html?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, "/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
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 _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 _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 _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 _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 _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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *fbuf;
|
||||||
|
AQH_SERVICE *sv;
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
sv=AQH_ModService_GetService(m);
|
||||||
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAA(fbuf, "%s%spages%s", AQH_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _layoutFromString(const char *s)
|
||||||
|
{
|
||||||
|
if (s && *s) {
|
||||||
|
if (strcasecmp(s, "none")==0)
|
||||||
|
return MY_LAYOUT_NONE;
|
||||||
|
else if (strcasecmp(s, "horizontal")==0)
|
||||||
|
return MY_LAYOUT_HORIZONTAL;
|
||||||
|
else if (strcasecmp(s, "vertical")==0)
|
||||||
|
return MY_LAYOUT_VERTICAL;
|
||||||
|
}
|
||||||
|
return MY_LAYOUT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
29
apps/aqhome-cgi/modules/devices/mdevices_page.h
Normal file
29
apps/aqhome-cgi/modules/devices/mdevices_page.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef AQHOME_CGI_MDEVICES_PAGE_H
|
||||||
|
#define AQHOME_CGI_MDEVICES_PAGE_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_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -86,13 +86,13 @@ void AQH_ModDevices_RunSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *se
|
|||||||
|
|
||||||
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
if (sValueName && *sValueName) {
|
if (sValueName && *sValueName) {
|
||||||
GBAS(pbuf, "Location: /aqbt/devices/value.html?device=");
|
GBAS(pbuf, "Location: value.html?device=");
|
||||||
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
||||||
GBAS(pbuf, "&value=");
|
GBAS(pbuf, "&value=");
|
||||||
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
|
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GBAS(pbuf, "Location: /aqbt/devices/values.html?device=");
|
GBAS(pbuf, "Location: values.html?device=");
|
||||||
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
||||||
}
|
}
|
||||||
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
||||||
|
|||||||
@@ -26,10 +26,6 @@
|
|||||||
#include <gwenhywfar/timestamp.h>
|
#include <gwenhywfar/timestamp.h>
|
||||||
#include <gwenhywfar/text.h>
|
#include <gwenhywfar/text.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -42,17 +38,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum {
|
|
||||||
VALUEGRAPH_PERIOD_4H=1,
|
|
||||||
VALUEGRAPH_PERIOD_1D,
|
|
||||||
VALUEGRAPH_PERIOD_1W,
|
|
||||||
VALUEGRAPH_PERIOD_1M,
|
|
||||||
VALUEGRAPH_PERIOD_6M,
|
|
||||||
VALUEGRAPH_PERIOD_12M,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
* vars
|
* vars
|
||||||
* ------------------------------------------------------------------------------------------------
|
* ------------------------------------------------------------------------------------------------
|
||||||
@@ -97,12 +82,8 @@ static void _createGraph(AQH_DATACLIENT *dc,
|
|||||||
const MY_GRAPH_PARAMS *graphParams,
|
const MY_GRAPH_PARAMS *graphParams,
|
||||||
const char *graphTitle, int precision, const char *curveLabel,
|
const char *graphTitle, int precision, const char *curveLabel,
|
||||||
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
|
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
|
||||||
static int _fileIsCurrent(const char *sPath, int seconds);
|
|
||||||
static AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision);
|
static AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision);
|
||||||
static void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf);
|
static void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf);
|
||||||
static AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
|
|
||||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
|
|
||||||
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
|
|
||||||
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s);
|
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s);
|
||||||
|
|
||||||
|
|
||||||
@@ -167,11 +148,10 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
if (value) {
|
if (value) {
|
||||||
GWEN_BUFFER *fbuf;
|
GWEN_BUFFER *fbuf;
|
||||||
int rv;
|
|
||||||
|
|
||||||
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
|
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
|
||||||
if (!_fileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
|
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
|
||||||
DBG_DEBUG(NULL, "Creating graph");
|
DBG_DEBUG(NULL, "Creating graph");
|
||||||
_createGraph(dc,
|
_createGraph(dc,
|
||||||
value,
|
value,
|
||||||
@@ -184,23 +164,7 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
100000);
|
100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1) {
|
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
|
||||||
GWEN_BUFFER *ibuf;
|
|
||||||
|
|
||||||
ibuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
|
||||||
// return file
|
|
||||||
rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fbuf), ibuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error reading \"%s\" (%d)", GWEN_Buffer_GetStart(fbuf), rv);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GWEN_Buffer_Reset(dbuf);
|
|
||||||
GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf));
|
|
||||||
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: image/png");
|
|
||||||
AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE);
|
|
||||||
}
|
|
||||||
GWEN_Buffer_free(ibuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
GWEN_Buffer_free(fbuf);
|
GWEN_Buffer_free(fbuf);
|
||||||
AQH_Value_free(value);
|
AQH_Value_free(value);
|
||||||
@@ -212,25 +176,6 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _fileIsCurrent(const char *sPath, int seconds)
|
|
||||||
{
|
|
||||||
struct stat sb;
|
|
||||||
time_t t1;
|
|
||||||
|
|
||||||
if (lstat(sPath, &sb)==-1) {
|
|
||||||
DBG_ERROR(NULL, "Error on lstat(%s): %s (%d)", sPath, strerror(errno), errno);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
t1=time(0);
|
|
||||||
if ((t1-sb.st_mtime)<(time_t) seconds) {
|
|
||||||
DBG_DEBUG(NULL, "File %s is current", sPath);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _createGraph(AQH_DATACLIENT *dc,
|
void _createGraph(AQH_DATACLIENT *dc,
|
||||||
const AQH_VALUE *v,
|
const AQH_VALUE *v,
|
||||||
@@ -252,7 +197,7 @@ void _createGraph(AQH_DATACLIENT *dc,
|
|||||||
tsBegin=time(0)-(graphParams->startTimeDiff);
|
tsBegin=time(0)-(graphParams->startTimeDiff);
|
||||||
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
|
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
|
||||||
|
|
||||||
dpList=_requestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
|
dpList=AQH_ModDevices_RequestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
|
||||||
if (dpList) {
|
if (dpList) {
|
||||||
DBG_DEBUG(NULL, "Adding data for %s", sValue);
|
DBG_DEBUG(NULL, "Adding data for %s", sValue);
|
||||||
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
|
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
|
||||||
@@ -309,63 +254,13 @@ void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_
|
|||||||
|
|
||||||
/* var name */
|
/* var name */
|
||||||
s=AQH_Value_GetNameForSystem(v);
|
s=AQH_Value_GetNameForSystem(v);
|
||||||
GWEN_Text_EscapeToBuffer(s, dbuf);
|
AQH_ModService_EscapeToBuffer(s, dbuf);
|
||||||
|
|
||||||
GBAA(dbuf, "-%s.png", graphParams->name);
|
GBAA(dbuf, "-%s.png", graphParams->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
|
|
||||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
|
|
||||||
{
|
|
||||||
uint64_t *dataPoints;
|
|
||||||
uint64_t recvdNum;
|
|
||||||
|
|
||||||
dataPoints=malloc(num*sizeof(uint64_t)*2);
|
|
||||||
|
|
||||||
recvdNum=AQH_DataClient_GetPeriodData(dc, valueName, dataPoints, num, tsBegin, tsEnd);
|
|
||||||
if (recvdNum>0) {
|
|
||||||
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
|
||||||
|
|
||||||
dpList=_createDataPairListFromDataPoints(dataPoints, recvdNum);
|
|
||||||
free(dataPoints);
|
|
||||||
return dpList;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "No data received for %s", valueName);
|
|
||||||
free(dataPoints);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues)
|
|
||||||
{
|
|
||||||
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
|
||||||
uint64_t i;
|
|
||||||
|
|
||||||
DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues);
|
|
||||||
dpList=AQDG_Graph_DataPair_List_new();
|
|
||||||
for(i=0; i<numValues; i++) {
|
|
||||||
AQDG_GRAPH_DATAPAIR *dp;
|
|
||||||
double timestamp;
|
|
||||||
union {double f; uint64_t i;} u;
|
|
||||||
|
|
||||||
timestamp=(double)(*(dataPoints++));
|
|
||||||
u.i=*(dataPoints++);
|
|
||||||
dp=AQDG_Graph_DataPair_new();
|
|
||||||
AQDG_Graph_DataPair_SetValueX(dp, timestamp);
|
|
||||||
AQDG_Graph_DataPair_SetValueY(dp, u.f);
|
|
||||||
AQDG_Graph_DataPair_List_Add(dp, dpList);
|
|
||||||
}
|
|
||||||
AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
|
|
||||||
return dpList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const MY_GRAPH_PARAMS *_getParamsByName(const char *s)
|
const MY_GRAPH_PARAMS *_getParamsByName(const char *s)
|
||||||
{
|
{
|
||||||
const MY_GRAPH_PARAMS *p;
|
const MY_GRAPH_PARAMS *p;
|
||||||
|
|||||||
Reference in New Issue
Block a user