Files
aqhomecontrol/apps/aqhome-cgi/modules/mdevices.c
Martin Preuss d0c8b3b284 let setData use double values instead of strings.
this allows for storing value set with setData which can then be used in
the cgi module to retrieve the last value set.
2025-10-07 23:50:50 +02:00

659 lines
20 KiB
C

/****************************************************************************
* 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.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
#define P_DEVICEREAD AQH_MODDEVICES_PERMS_DEVICEREAD
#define P_VALUEREAD AQH_MODDEVICES_PERMS_VALUEREAD
#define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _createPermDefList(AQH_MODULE *m);
static void _createRoleList(AQH_MODULE *m);
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqValuesGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _runIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
static void _runValues(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
static void _runSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
static void _writeValueListToTable(AQH_DATACLIENT *dc, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf);
static void _writeValueToTable(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static uint32_t _colorFromHexString(const char *s);
static void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
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 void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* vars
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
{"index.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD, _handleRqIndexGet},
{"values.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesGet},
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
{NULL, 0, 0, NULL}
};
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
}
int AQH_ModDevices_Create(AQH_SERVICE *sv)
{
AQH_MODULE *m;
int rv;
m=AQH_Module_new();
AQH_Module_SetName(m, "devices");
AQH_Module_SetDescr(m, "device module");
AQH_Module_SetGuestPerms(m, 0);
_createPermDefList(m);
_createRoleList(m);
rv=AQH_Service_AddModule(sv, m);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Module_free(m);
return rv;
}
void _createPermDefList(AQH_MODULE *m)
{
AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_PermDef_List_new();
AQH_ModService_AddPermDef(permDefList, "DeviceRead", 0x001, "Read and list devices");
AQH_ModService_AddPermDef(permDefList, "DeviceWrite", 0x002, "Modify devices");
AQH_ModService_AddPermDef(permDefList, "DeviceAdd", 0x004, "Add devices");
AQH_ModService_AddPermDef(permDefList, "DeviceDel", 0x008, "Remove devices");
AQH_ModService_AddPermDef(permDefList, "ValueRead", 0x010, "Read and list values");
AQH_ModService_AddPermDef(permDefList, "ValueWrite", 0x020, "Modify values");
AQH_ModService_AddPermDef(permDefList, "ValueAdd", 0x040, "Add values");
AQH_ModService_AddPermDef(permDefList, "ValueDel", 0x080, "Remove values");
AQH_ModService_AddPermDef(permDefList, "ValueSet", 0x100, "Set values");
AQH_Module_SetPermDefList(m, permDefList);
}
void _createRoleList(AQH_MODULE *m)
{
AQH_ROLE_LIST *roleList;
int id=0;
roleList=AQH_Role_List_new();
AQH_ModService_AddRole(roleList, id++, "Reader",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_VALUEREAD,
"Read devices and values");
AQH_ModService_AddRole(roleList, id++, "Writer",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_DEVICEWRITE |
AQH_MODDEVICES_PERMS_DEVICEADD |
AQH_MODDEVICES_PERMS_DEVICEDEL |
AQH_MODDEVICES_PERMS_VALUEREAD |
AQH_MODDEVICES_PERMS_VALUEWRITE |
AQH_MODDEVICES_PERMS_VALUEADD |
AQH_MODDEVICES_PERMS_VALUEDEL |
AQH_MODDEVICES_PERMS_VALUESET,
"Read and write devices and values");
AQH_ModService_AddRole(roleList, id++, "Setter",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_VALUEREAD |
AQH_MODDEVICES_PERMS_VALUESET,
"Set values");
AQH_Module_SetRoleList(m, roleList);
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
AQH_ModService_HandleRequestWithTable(m, rq, session, sLastPathElem, _requestTable);
return AQCGI_SendResponse(rq);
}
void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, _runIndex, dbuf);
}
void _handleRqValuesGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, _runValues, dbuf);
}
void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, _runSetData, dbuf);
}
void _runIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
AQH_DEVICE_LIST *deviceList;
AQH_DEVICE *device;
uint32_t perms;
perms=AQH_ModService_GetUserPerms(m);
deviceList=AQH_DataClient_GetDevices(dc, NULL);
if (deviceList==NULL) {
DBG_ERROR(NULL, "No device received");
GBAS(dbuf, "<p>No devices.</p>");
return;
}
GBAS(dbuf, "<h1>Devices</h1>\n");
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>\n"
"<tr>"
"<th>Name For System</th>"
"<th>Room</th>"
"<th>Location</th>"
"<th>Description</th>"
#if 0
"<th>Type</th>"
"<th>Driver</th>"
"<th>Name</th>"
"<th>GUI Name</th>"
"<th>Manufacturer</th>"
#endif
"</tr>\n"
"</thead>\n"
"<tbody>\n");
device=AQH_Device_List_First(deviceList);
while(device) {
const char *s;
GBAA(dbuf, "<tr>");
/* name for system */
s=AQH_Device_GetNameForSystem(device);
if (perms & AQH_MODDEVICES_PERMS_VALUEREAD) {
GBAS(dbuf,"<td><a href=\"values.html?device=");
GWEN_Text_EscapeToBufferTolerant(s, dbuf);
GBAA(dbuf,"\">%s</a></td>", s?s:"");
}
else
GBAA(dbuf,"<td>%s</td>", s?s:"");
/* room */
s=AQH_Device_GetRoomName(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* location */
s=AQH_Device_GetLocation(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* description */
s=AQH_Device_GetDescription(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
#if 0
/* device type */
s=AQH_Device_GetDeviceType(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* driver */
s=AQH_Device_GetDriver(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* short device name */
s=AQH_Device_GetName(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* GUI name for device */
s=AQH_Device_GetNameForGui(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* manufacturer */
s=AQH_Device_GetManufacturer(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
#endif
GBAA(dbuf, "</tr>");
device=AQH_Device_List_Next(device);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
AQH_Device_List_free(deviceList);
}
void _runValues(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
if (!(sDeviceName && *sDeviceName))
_runIndex(m, rq, session, dc, dbuf);
else {
AQH_VALUE_LIST *valueList;
valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0);
if (valueList && AQH_Value_List_GetCount(valueList)) {
GBAA(dbuf,"<h1>Values for Device %s</h1>\n", sDeviceName);
GBAS(dbuf,"<form action=\"setdata.html\" method=\"post\">\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"device\" value=\"%s\">\n", sDeviceName);
_writeValueListToTable(dc, valueList, dbuf);
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
GBAS(dbuf, "</form>\n\n");
}
else {
GBAS(dbuf,"<p>No values.</p>\n");
}
AQH_Value_List_free(valueList);
}
}
void _runSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sDeviceName;
AQH_VALUE_LIST *valueList;
/* sample data */
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sDeviceName=dbPost?GWEN_DB_GetCharValue(dbPost, "device", 0, NULL):NULL;
valueList=sDeviceName?AQH_DataClient_GetValues(dc, sDeviceName, 0):NULL;
if (valueList && AQH_Value_List_GetCount(valueList)) {
const AQH_VALUE *value;
value=AQH_Value_List_First(valueList);
while(value) {
if (AQH_Value_GetValueType(value)==AQH_ValueType_Actor) {
const char *sValueName;
const char *sValue;
sValueName=AQH_Value_GetName(value);
sValue=GWEN_DB_GetCharValue(dbPost, sValueName, 0, NULL);
if (sValueName && *sValueName) {
DBG_ERROR(NULL, "Setting value %s to %s", sValueName?sValueName:"no name", sValue?sValue:"no value");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW: _setRgbwData(dc, value, sValue); break;
case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sValue); break;
case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sValue); break;
default:
break;
} /* switch */
} /* if (sValueName) */
}
value=AQH_Value_List_Next(value);
} /* while */
}
AQH_Value_List_free(valueList);
if (sDeviceName && *sDeviceName) {
GWEN_BUFFER *pbuf;
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAS(pbuf, "Location: /aqbt/devices/values.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, 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 _writeValueListToTable(AQH_DATACLIENT *dc, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf)
{
const AQH_VALUE *value;
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>"
"<tr>"
"<th>Name</th>"
"<th>Type</th>"
"<th>Modality</th>"
#if 0
"<th>Driver</th>"
"<th>Device</th>"
"<th>Name for System</th>"
#endif
"<th>Action</th>"
"</tr>"
"</thead>\n"
"<tbody>\n");
value=AQH_Value_List_First(valueList);
while(value) {
_writeValueToTable(dc, value, dbuf);
value=AQH_Value_List_Next(value);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
}
void _writeValueToTable(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *s;
GBAS(dbuf, "<tr>");
s=AQH_Value_GetName(value);
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_ValueType_toString(AQH_Value_GetValueType(value));
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_ValueModality_toString(AQH_Value_GetModality(value));
GBAA(dbuf, "<td>%s</td>", s?s:"");
#if 0
s=AQH_Value_GetDriver(value);
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_Value_GetDeviceNameForSystem(value);
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_Value_GetNameForSystem(value);
GBAA(dbuf, "<td>%s</td>", s?s:"");
#endif
GBAS(dbuf, "<td>");
_addLastValueToForm(dc, value, dbuf);
GBAS(dbuf, "</td>");
GBAA(dbuf, "</tr>\n");
}
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueName;
sValueName=AQH_Value_GetName(value);
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW:
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"refresh\"/>", sValueName);
break;
case AQH_ValueModality_OnOff:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"</select>",
sValueName);
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"<option value=\"auto\">auto</option>"
"</select>",
sValueName);
break;
default:
break;
}
}
uint32_t _colorFromHexString(const char *s)
{
uint32_t colorIn=0;
uint32_t colorOut;
while(*s && *s<33)
s++;
if (*s=='#')
s++;
while(*s) {
uint c;
c=(*s)-'0';
if (c>9)
c-=7;
colorIn<<=4;
colorIn|=c & 0xf;
s++;
}
/* hex 00RRGGBB -> GGRRWWBB */
colorOut=(colorIn & 0x00ff0000) | ((colorIn & 0x00ff00)<<16) | (colorIn & 0x0000ff);
return colorOut;
}
void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
uint32_t color;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
color=_colorFromHexString(sValue);
DBG_ERROR(NULL, "Send value [#%08x] to %s", color, sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, (double) color);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
}
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_ERROR(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else {
}
}
}
void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
if (strcasecmp(sValue, "unchanged")==0) {
DBG_ERROR(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "auto")==0) {
DBG_ERROR(NULL, "Send value 2 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 2.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else {
DBG_ERROR(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName);
}
}
}
void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueSystemName;
const char *sValueName;
uint64_t dataPoints[2];
uint64_t recvdNum;
// uint64_t timestamp;
union {double f; uint64_t i;} u;
int intVal;
sValueSystemName=AQH_Value_GetNameForSystem(value);
sValueName=AQH_Value_GetName(value);
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
if (recvdNum>0) {
// timestamp=dataPoints[0];
u.i=dataPoints[1];
intVal=(int) u.f;
}
else {
u.i=0;
intVal=-1;
}
if (AQH_Value_GetValueType(value)==AQH_ValueType_Actor) {
DBG_ERROR(NULL, "Adding actor");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW:
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"#%08x\"/>", sValueName, (unsigned int) u.f);
break;
case AQH_ValueModality_OnOff:
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
break;
default:
// GBAA(dbuf, "<input type=\"text\" name=\"%s\" value=\"%.2f\"/>", sValueName, u.f);
GBAA(dbuf, "%.2f", u.f);
break;
} /* switch */
} /* if actor */
else {
DBG_ERROR(NULL, "Adding sensor (%s=%.2f)", sValueName, u.f);
GBAA(dbuf, "%.2f", u.f);
}
}