Files
aqhomecontrol/apps/aqhome-mqttlog/xmlread.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

483 lines
13 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 "./xmlread.h"
#include "./server_p.h"
#include "aqhome-mqttlog/types/topic.h"
#include "aqhome-mqttlog/types/value.h"
#include "aqhome-mqttlog/types/translation.h"
#include <aqhome/api.h>
#include <aqhome/aqhome.h>
#include <gwenhywfar/endpoint_multilayer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/xml.h>
#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQHMQTT_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
static int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList);
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList);
static AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
static AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode);
static AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode);
static AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
static AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename)
{
if (o) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo) {
int rv;
rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE);
if (rv<0) {
DBG_ERROR(NULL, "File \"%s\" does not exists, writing later", sFilename);
return NULL;
}
else {
AQHMQTT_DEVICE_LIST *deviceList;
deviceList=AQHMQTT_Device_List_new();
rv=_readDeviceFileToList(sFilename, deviceList);
if (rv<0) {
DBG_ERROR(NULL, "Could not read file \"%s\" (empty?)", sFilename);
AQHMQTT_Device_List_free(deviceList);
return NULL;
}
if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
AQHMQTT_Device_List_free(deviceList);
return NULL;
}
return deviceList;
}
}
}
return NULL;
}
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o)
{
if (o) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo) {
GWEN_STRINGLIST *sl;
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/mqtt", "*.xml");
if (sl) {
AQHMQTT_DEVICE_LIST *deviceList;
deviceList=_readDeviceFiles(sl);
GWEN_StringList_free(sl);
if (deviceList==NULL) {
DBG_INFO(NULL, "Error reading data device files");
return NULL;
}
return deviceList;
}
else {
DBG_ERROR(NULL, "No data device files");
}
}
}
return NULL;
}
AQHMQTT_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl)
{
GWEN_STRINGLISTENTRY *se;
AQHMQTT_DEVICE_LIST *deviceList;
deviceList=AQHMQTT_Device_List_new();
se=GWEN_StringList_FirstEntry(sl);
while(se) {
const char *s;
s=GWEN_StringListEntry_Data(se);
if (s && *s) {
int rv;
DBG_INFO(NULL, "Reading device file \"%s\"", s);
rv=_readDeviceFileToList(s, deviceList);
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
DBG_WARN(NULL, "Error reading device file \"%s\" (%d), ignoring", s, rv);
}
}
se=GWEN_StringListEntry_Next(se);
}
if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
AQHMQTT_Device_List_free(deviceList);
return NULL;
}
return deviceList;
}
int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList)
{
GWEN_XMLNODE *rootNode;
GWEN_XMLNODE *deviceListNode;
int rv;
rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL);
rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
if (rv<0) {
DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv);
GWEN_XMLNode_free(rootNode);
return rv;
}
deviceListNode=GWEN_XMLNode_FindFirstTag(rootNode, "devices", NULL, NULL);
if (deviceListNode==NULL)
deviceListNode=rootNode;
rv=_readXmlDevices(deviceListNode, deviceList);
if (rv<0 && rv!=GWEN_ERROR_NO_DATA) {
DBG_ERROR(AQH_LOGDOMAIN, "Error reading devices from file \"%s\" (%d)", sFilename, rv);
GWEN_XMLNode_free(rootNode);
return rv;
}
GWEN_XMLNode_free(rootNode);
return 0;
}
int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList)
{
GWEN_XMLNODE *deviceNode;
deviceNode=GWEN_XMLNode_FindFirstTag(deviceListNode, "device", NULL, NULL);
if (deviceNode) {
while(deviceNode) {
const char *driverName;
driverName=GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL);
if (driverName && *driverName && strcasecmp(driverName, "mqtt")==0) {
AQHMQTT_DEVICE *device;
device=_readXmlDevice(deviceNode);
if (device==NULL) {
DBG_INFO(NULL, "Error reading device from XML");
return GWEN_ERROR_BAD_DATA;
}
else {
const char *sDeviceId;
const char *sDeviceName;
sDeviceId=AQHMQTT_Device_GetId(device);
sDeviceName=AQHMQTT_Device_GetName(device);
if (sDeviceId && *sDeviceId) {
DBG_ERROR(NULL, "Adding device \"%s\" (%s) to list", sDeviceId, sDeviceName?sDeviceName:"<no type name>");
}
else {
DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
}
AQHMQTT_Device_List_Add(device, deviceList);
}
}
else {
DBG_INFO(NULL, "Device is not an MQTT device, ignoring");
}
deviceNode=GWEN_XMLNode_FindNextTag(deviceNode, "device", NULL, NULL);
} /* while */
return 0;
}
else {
DBG_INFO(NULL, "No <device> element found");
return GWEN_ERROR_NO_DATA;
}
}
AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
{
AQHMQTT_DEVICE *device;
GWEN_XMLNODE *topicsNode;
device=AQHMQTT_Device_new();
AQHMQTT_Device_SetId(device, GWEN_XMLNode_GetProperty(deviceNode, "id", NULL));
AQHMQTT_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
AQHMQTT_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));
topicsNode=GWEN_XMLNode_FindFirstTag(deviceNode, "mqtttopics", NULL, NULL);
if (topicsNode) {
AQHMQTT_TOPIC_LIST *topicList;
topicList=_readXmlTopicList(topicsNode);
if (topicList)
AQHMQTT_Device_SetTopicList(device, topicList);
else {
DBG_INFO(NULL, "No mqtt topics read");
AQHMQTT_Device_free(device);
return NULL;
}
}
else {
DBG_INFO(NULL, "No <mqtttopics> element");
AQHMQTT_Device_free(device);
return NULL;
}
return device;
}
AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode)
{
AQHMQTT_TOPIC_LIST *topicList;
GWEN_XMLNODE *topicNode;
topicList=AQHMQTT_Topic_List_new();
topicNode=GWEN_XMLNode_FindFirstTag(parentNode, "mqtttopic", NULL, NULL);
while(topicNode) {
AQHMQTT_TOPIC *topic=_readXmlTopic(topicNode);
if (topic)
AQHMQTT_Topic_List_Add(topic, topicList);
else {
DBG_INFO(NULL, "Error reading <mqtttopic> element");
AQHMQTT_Topic_List_free(topicList);
return NULL;
}
topicNode=GWEN_XMLNode_FindNextTag(topicNode, "mqtttopic", NULL, NULL);
}
if (AQHMQTT_Topic_List_GetCount(topicList)<1) {
AQHMQTT_Topic_List_free(topicList);
return NULL;
}
return topicList;
}
AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode)
{
AQHMQTT_TOPIC *topic;
GWEN_XMLNODE *valuesNode;
int i;
const char *s;
topic=AQHMQTT_Topic_new();
s=GWEN_XMLNode_GetProperty(topicNode, "type", NULL);
i=AQHMQTT_TopicType_fromString(s);
if (i==AQHMQTT_TopicType_Unknown) {
DBG_ERROR(NULL, "Invalid topic type \"%s\"", s?s:"<empty>");
AQHMQTT_Topic_free(topic);
return NULL;
}
AQHMQTT_Topic_SetTopicType(topic, i);
s=GWEN_XMLNode_GetProperty(topicNode, "direction", NULL);
i=AQHMQTT_TopicDir_fromString(s);
if (i==AQHMQTT_TopicDir_Unknown) {
DBG_ERROR(NULL, "Invalid topic direction \"%s\"", s?s:"<empty>");
AQHMQTT_Topic_free(topic);
return NULL;
}
AQHMQTT_Topic_SetDirection(topic, i);
AQHMQTT_Topic_SetName(topic, GWEN_XMLNode_GetProperty(topicNode, "name", NULL));
AQHMQTT_Topic_SetTopic(topic, GWEN_XMLNode_GetCharValue(topicNode, "topic", NULL));
AQHMQTT_Topic_SetMask(topic, GWEN_XMLNode_GetCharValue(topicNode, "mask", NULL));
AQHMQTT_Topic_SetBeforeId(topic, GWEN_XMLNode_GetCharValue(topicNode, "beforeId", NULL));
AQHMQTT_Topic_SetAfterId(topic, GWEN_XMLNode_GetCharValue(topicNode, "afterId", NULL));
valuesNode=GWEN_XMLNode_FindFirstTag(topicNode, "values", NULL, NULL);
if (valuesNode) {
AQHMQTT_VALUE_LIST *valueList;
valueList=_readXmlValueList(valuesNode);
if (valueList)
AQHMQTT_Topic_SetValueList(topic, valueList);
else {
DBG_INFO(NULL, "No values read");
AQHMQTT_Topic_free(topic);
return NULL;
}
}
else {
DBG_INFO(NULL, "No <values> element");
AQHMQTT_Topic_free(topic);
return NULL;
}
return topic;
}
AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode)
{
AQHMQTT_VALUE_LIST *valueList;
GWEN_XMLNODE *valueNode;
valueList=AQHMQTT_Value_List_new();
valueNode=GWEN_XMLNode_FindFirstTag(parentNode, "value", NULL, NULL);
while(valueNode) {
AQHMQTT_VALUE *value=_readXmlValue(valueNode);
if (value)
AQHMQTT_Value_List_Add(value, valueList);
else {
DBG_INFO(NULL, "Error reading <value> element");
AQHMQTT_Value_List_free(valueList);
return NULL;
}
valueNode=GWEN_XMLNode_FindNextTag(valueNode, "value", NULL, NULL);
}
if (AQHMQTT_Value_List_GetCount(valueList)<1) {
AQHMQTT_Value_List_free(valueList);
return NULL;
}
return valueList;
}
AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
{
AQHMQTT_VALUE *value;
GWEN_XMLNODE *translationNode;
const char *s;
int i;
value=AQHMQTT_Value_new();
AQHMQTT_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
AQHMQTT_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
AQHMQTT_Value_SetPath(value, GWEN_XMLNode_GetProperty(valueNode, "path", NULL));
s=GWEN_XMLNode_GetProperty(valueNode, "type", NULL);
i=AQHMQTT_ValueType_fromString(s);
if (i==AQHMQTT_ValueType_Unknown) {
DBG_ERROR(NULL, "Invalid value type \"%s\"", s?s:"<empty>");
AQHMQTT_Value_free(value);
return NULL;
}
AQHMQTT_Value_SetValueType(value, i);
translationNode=GWEN_XMLNode_FindFirstTag(valueNode, "translations", NULL, NULL);
if (translationNode) {
AQHMQTT_TRANSLATION_LIST *translationList;
translationList=_readXmlTranslationList(translationNode);
if (translationList) {
DBG_INFO(NULL, "Translations read");
AQHMQTT_Value_SetTranslationList(value, translationList);
}
}
else {
DBG_INFO(NULL, "No <translations> element");
}
return value;
}
AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode)
{
AQHMQTT_TRANSLATION_LIST *translationList;
GWEN_XMLNODE *translationNode;
translationList=AQHMQTT_Translation_List_new();
translationNode=GWEN_XMLNode_FindFirstTag(parentNode, "translation", NULL, NULL);
while(translationNode) {
AQHMQTT_TRANSLATION *translation=_readXmlTranslation(translationNode);
if (translation)
AQHMQTT_Translation_List_Add(translation, translationList);
else {
DBG_INFO(NULL, "Error reading <translation> element");
AQHMQTT_Translation_List_free(translationList);
return NULL;
}
translationNode=GWEN_XMLNode_FindNextTag(translationNode, "translation", NULL, NULL);
}
if (AQHMQTT_Translation_List_GetCount(translationList)<1) {
AQHMQTT_Translation_List_free(translationList);
return NULL;
}
return translationList;
}
AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode)
{
int aqhValue;
const char *sDriverValue;
aqhValue=GWEN_XMLNode_GetIntProperty(translationNode, "aqhValue", 0);
sDriverValue=GWEN_XMLNode_GetProperty(translationNode, "driverValue", NULL);
if (sDriverValue && *sDriverValue) {
AQHMQTT_TRANSLATION *translation;
translation=AQHMQTT_Translation_new();
AQHMQTT_Translation_SetAqhValue(translation, aqhValue);
AQHMQTT_Translation_SetDriverValue(translation, sDriverValue);
return translation;
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "Either AqhValue or DriverValue missing in device description file");
return NULL;
}
}