We will now have a broker (aqhome-data) which stores data and distributes value change messages among connected clients. aqhomed will connect to that broker and send its values there. aqhome-mqtt will also connect to the broker and send its values there. Other clients can later connect to check for changes and react according to rules.
341 lines
11 KiB
C
341 lines
11 KiB
C
/****************************************************************************
|
|
* This file is part of the project AqHome.
|
|
* AqHome (c) by 2023 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 "./u_values.h"
|
|
#include "./u_objects.h"
|
|
#include "./aqhomehttp.h"
|
|
|
|
#include <gwenhywfar/gwenhywfar.h>
|
|
#include <gwenhywfar/debug.h>
|
|
#include <gwenhywfar/i18n.h>
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* defines
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* forward declarations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
|
|
static void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
static void _setFromObject(AQH_VALUE *value, const AQH_VALUE *srcValue);
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* implementations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
AQH_HTTP_URLHANDLER *AQH_ValuesHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
{
|
|
AQH_HTTP_URLHANDLER *uh;
|
|
|
|
uh=AQH_ObjectsHttpUrlHandler_new(sv,
|
|
AQHOME_HTTP_PERMS_LIST_VALUES,
|
|
AQHOME_HTTP_PERMS_ADD_VALUE,
|
|
AQHOME_HTTP_PERMS_DEL_VALUE,
|
|
AQHOME_HTTP_PERMS_EDIT_VALUE,
|
|
"/values/list");
|
|
AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(uh, _addOrEditObject);
|
|
AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(uh, _findObjectByIdAndReturnAsDb);
|
|
AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(uh, _writeAddPage);
|
|
AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(uh, _writeEditPage);
|
|
AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(uh, _listObjectsIntoBuffer);
|
|
|
|
return uh;
|
|
}
|
|
|
|
|
|
|
|
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
AQH_STORAGE *sto;
|
|
AQH_VALUE *newValue;
|
|
const char *valueName;
|
|
int rv;
|
|
|
|
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
sto=AqHomeHttpService_GetStorage(sv);
|
|
|
|
newValue=AQH_Value_fromDb(db);
|
|
|
|
valueName=AQH_Value_GetNameForSystem(newValue);
|
|
if (!(valueName && *valueName)) {
|
|
DBG_INFO(NULL, "Missing value name");
|
|
AQH_Value_free(newValue);
|
|
return GWEN_ERROR_INVALID;
|
|
}
|
|
|
|
rv=AqHomeHttpService_LockStorage(sv);
|
|
if (rv<0) {
|
|
DBG_ERROR(NULL, "Error locking storage");
|
|
AQH_Value_free(newValue);
|
|
return GWEN_ERROR_IO;
|
|
}
|
|
|
|
if (id>0) {
|
|
AQH_VALUE *value;
|
|
|
|
DBG_INFO(NULL, "Edit existing value");
|
|
value=AQH_Storage_GetValueById(sto, id);
|
|
if (value==NULL) {
|
|
AqHomeHttpService_UnlockStorage(sv);
|
|
DBG_ERROR(NULL, "Value %d not found", id);
|
|
AQH_Value_free(newValue);
|
|
return GWEN_ERROR_NOT_FOUND;
|
|
}
|
|
AQH_Value_SetId(value, id);
|
|
_setFromObject(value, newValue);
|
|
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
}
|
|
else {
|
|
AQH_VALUE *value;
|
|
|
|
DBG_INFO(NULL, "Adding new value");
|
|
value=AQH_Storage_GetValueByName(sto, valueName);
|
|
if (value) {
|
|
AqHomeHttpService_UnlockStorage(sv);
|
|
DBG_ERROR(NULL, "Value %s exists", valueName);
|
|
AQH_Value_free(newValue);
|
|
return GWEN_ERROR_FOUND;
|
|
}
|
|
value=AQH_Value_new();
|
|
AQH_Value_SetId(value, 0);
|
|
_setFromObject(value, newValue);
|
|
AQH_Storage_AddValue(sto, value);
|
|
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
}
|
|
AQH_Value_free(newValue);
|
|
|
|
rv=AqHomeHttpService_UnlockStorage(sv);
|
|
if (rv<0) {
|
|
DBG_ERROR(NULL, "Error unlocking storage");
|
|
return GWEN_ERROR_IO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void _setFromObject(AQH_VALUE *value, const AQH_VALUE *srcValue)
|
|
{
|
|
AQH_Value_SetName(value, AQH_Value_GetName(srcValue));
|
|
AQH_Value_SetTopicId(value, AQH_Value_GetTopicId(srcValue));
|
|
AQH_Value_SetValueType(value, AQH_Value_GetValueType(srcValue));
|
|
AQH_Value_SetValueUnits(value, AQH_Value_GetValueUnits(srcValue));
|
|
AQH_Value_SetDataPath(value, AQH_Value_GetDataPath(srcValue));
|
|
}
|
|
|
|
|
|
|
|
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
AQH_STORAGE *sto;
|
|
const AQH_VALUE *value;
|
|
|
|
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
sto=AqHomeHttpService_GetStorage(sv);
|
|
|
|
value=AQH_Storage_GetValueById(sto, id);
|
|
if (value) {
|
|
GWEN_DB_NODE *db;
|
|
|
|
db=GWEN_DB_Group_new("value");
|
|
AQH_Value_toDb(value, db);
|
|
return db;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
{
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
"<h2>%s</h2><br>\n"
|
|
"<form action=\"/values/add\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
I18N("Add Value"));
|
|
_writeEditingTable(uh, dbValues, pageBuf);
|
|
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
{
|
|
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
"<h2>%s</h2><br>\n"
|
|
"<form action=\"/values/edit/%lu\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
I18N("Edit Value"),
|
|
(long unsigned int)(dbValues?GWEN_DB_GetIntValue(dbValues, "id", 0, 0):0));
|
|
_writeEditingTable(uh, dbValues, pageBuf);
|
|
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
AQH_STORAGE *sto;
|
|
const AQH_MQTT_TOPIC_LIST *topicList;
|
|
unsigned long int selectedTopicId=0;
|
|
char numbuf[16];
|
|
|
|
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
sto=AqHomeHttpService_GetStorage(sv);
|
|
topicList=AQH_Storage_GetMqttTopicList(sto);
|
|
|
|
selectedTopicId=(unsigned long int)(dbValues?GWEN_DB_GetIntValue(dbValues, "TopicId", 0, 0):0);
|
|
snprintf(numbuf, sizeof(numbuf)-1, "%lu", selectedTopicId);
|
|
numbuf[sizeof(numbuf)-1]=0;
|
|
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
" <table>"
|
|
" <tr>"
|
|
" <td><label for=\"name\">%s: </label></td>"
|
|
" <td><input type=\"text\" name=\"name\" value=\"%s\" required></td>"
|
|
" </tr>",
|
|
I18N("Name"),
|
|
dbValues?GWEN_DB_GetCharValue(dbValues, "name", 0, ""):"");
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
"<tr><td><label for=\"topicId\">%s: </label></td>"
|
|
"<td><select id=\"topicId\" name=\"topicId\">",
|
|
I18N("Topic"));
|
|
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"0\">%s</option>", I18N("-- select topic --"));
|
|
|
|
if (topicList) {
|
|
const AQH_MQTT_TOPIC *topic;
|
|
|
|
topic=AQH_MqttTopic_List_First(topicList);
|
|
while(topic) {
|
|
const char *topicName;
|
|
|
|
topicName=AQH_MqttTopic_GetTopic(topic);
|
|
if (topicName && *topicName) {
|
|
snprintf(numbuf, sizeof(numbuf)-1, "%lu", (unsigned long int) AQH_MqttTopic_GetId(topic));
|
|
numbuf[sizeof(numbuf)-1]=0;
|
|
if (selectedTopicId && AQH_MqttTopic_GetId(topic)==selectedTopicId)
|
|
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\" selected>%s</option>", numbuf, topicName);
|
|
else
|
|
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\">%s</option>", numbuf, topicName);
|
|
}
|
|
topic=AQH_MqttTopic_List_Next(topic);
|
|
}
|
|
}
|
|
GWEN_Buffer_AppendString(pageBuf, "</select></td></tr>");
|
|
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
" <tr>"
|
|
" <td><label for=\"valueUnits\">%s: </label></td>"
|
|
" <td><input type=\"text\" name=\"valueUnits\" value=\"%s\"></td>"
|
|
" </tr>",
|
|
I18N("Units"),
|
|
dbValues?GWEN_DB_GetCharValue(dbValues, "valueUnits", 0, ""):"");
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
" <tr>"
|
|
" <td><label for=\"dataPath\">%s: </label></td>"
|
|
" <td><input type=\"text\" name=\"dataPath\" value=\"%s\"></td>"
|
|
" </tr>"
|
|
" </table>",
|
|
I18N("Data Path (JSON)"),
|
|
dbValues?GWEN_DB_GetCharValue(dbValues, "dataPath", 0, ""):"");
|
|
}
|
|
|
|
|
|
|
|
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
AQH_STORAGE *sto;
|
|
const AQH_VALUE_LIST *rl;
|
|
|
|
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
sto=AqHomeHttpService_GetStorage(sv);
|
|
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
"<h2>%s</h2>"
|
|
"<table class=\"dataTable\">"
|
|
"<thead>"
|
|
" <tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th></th></tr>"
|
|
"</thead>",
|
|
I18N("Values"),
|
|
I18N("Name"),
|
|
I18N("Topic"),
|
|
I18N("Units"),
|
|
I18N("Data Path"));
|
|
GWEN_Buffer_AppendString(pageBuf, "<tbody>");
|
|
rl=AQH_Storage_GetValueList(sto);
|
|
if (rl) {
|
|
const AQH_VALUE *value;
|
|
|
|
value=AQH_Value_List_First(rl);
|
|
while(value) {
|
|
long unsigned int id;
|
|
int topicId;
|
|
const char *name;
|
|
const char *topicName=NULL;
|
|
const char *valueUnits;
|
|
const char *dataPath;
|
|
const AQH_MQTT_TOPIC *topic=NULL;
|
|
|
|
id=(long unsigned int) AQH_Value_GetId(value);
|
|
topicId=(long unsigned int) AQH_Value_GetTopicId(value);
|
|
if (topicId>0)
|
|
topic=AQH_Storage_GetMqttTopicById(sto, topicId);
|
|
if (topic)
|
|
topicName=AQH_MqttTopic_GetTopic(topic);
|
|
|
|
name=AQH_Value_GetName(value);
|
|
valueUnits=AQH_Value_GetValueUnits(value);
|
|
dataPath=AQH_Value_GetDataPath(value);
|
|
GWEN_Buffer_AppendArgs(pageBuf,
|
|
"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
|
|
"<td><a href=\"/values/edit/%lu\">"
|
|
"<IMG src=\"/pics/edit.png\" width=32 height=32 align=left border=0></a></td>"
|
|
"</tr>",
|
|
name?name:"",
|
|
topicName?topicName:"",
|
|
valueUnits?valueUnits:"",
|
|
dataPath?dataPath:"",
|
|
id);
|
|
value=AQH_Value_List_Next(value);
|
|
}
|
|
}
|
|
GWEN_Buffer_AppendString(pageBuf, "</tbody>");
|
|
GWEN_Buffer_AppendArgs(pageBuf, "</table><a href=\"/values/add\">%s</a><br>", I18N("Add Value"));
|
|
}
|
|
|
|
|
|
|
|
|
|
|