468 lines
14 KiB
C
468 lines
14 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 "./xmlread.h"
|
|
#include "./aqhome_mqtt_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(AQHOME_MQTT *aqh, const GWEN_STRINGLIST *sl);
|
|
static int _readDeviceFileToList(AQHOME_MQTT *aqh, const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList);
|
|
static int _readXmlDevices(AQHOME_MQTT *aqh, GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList);
|
|
static AQHMQTT_DEVICE *_readXmlDevice(AQHOME_MQTT *aqh, GWEN_XMLNODE *deviceNode);
|
|
static AQHMQTT_TOPIC_LIST *_readXmlTopicList(AQHOME_MQTT *aqh, GWEN_XMLNODE *parentNode);
|
|
static AQHMQTT_TOPIC *_readXmlTopic(AQHOME_MQTT *aqh, GWEN_XMLNODE *topicNode);
|
|
static AQHMQTT_VALUE_LIST *_readXmlValueList(AQHOME_MQTT *aqh, GWEN_XMLNODE *parentNode);
|
|
static AQHMQTT_VALUE *_readXmlValue(AQHOME_MQTT *aqh, GWEN_XMLNODE *valueNode);
|
|
static AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(AQHOME_MQTT *aqh, GWEN_XMLNODE *parentNode);
|
|
static AQHMQTT_TRANSLATION *_readXmlTranslation(AQHOME_MQTT *aqh, GWEN_XMLNODE *translationNode);
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* implementations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDeviceFile(AQHOME_MQTT *aqh, const char *sFilename)
|
|
{
|
|
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(aqh, sFilename, deviceList);
|
|
if (rv<0) {
|
|
DBG_ERROR(NULL, "File \"%s\" not found", sFilename);
|
|
AQHMQTT_Device_List_free(deviceList);
|
|
return NULL;
|
|
}
|
|
|
|
if (AQHMQTT_Device_List_GetCount(deviceList)<1) {
|
|
AQHMQTT_Device_List_free(deviceList);
|
|
return NULL;
|
|
}
|
|
return deviceList;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDataDeviceFiles(AQHOME_MQTT *aqh)
|
|
{
|
|
GWEN_STRINGLIST *sl;
|
|
|
|
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/mqtt", "*.xml");
|
|
if (sl) {
|
|
AQHMQTT_DEVICE_LIST *deviceList;
|
|
|
|
deviceList=_readDeviceFiles(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, 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(aqh, 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(AQHOME_MQTT *aqh, GWEN_XMLNODE *translationNode)
|
|
{
|
|
const char *sAqhValue;
|
|
const char *sDriverValue;
|
|
|
|
sAqhValue=GWEN_XMLNode_GetProperty(translationNode, "aqhValue", NULL);
|
|
sDriverValue=GWEN_XMLNode_GetProperty(translationNode, "driverValue", NULL);
|
|
if (sAqhValue && *sAqhValue && sDriverValue && *sDriverValue) {
|
|
AQHMQTT_TRANSLATION *translation;
|
|
|
|
translation=AQHMQTT_Translation_new();
|
|
AQHMQTT_Translation_SetAqhValue(translation, sAqhValue);
|
|
AQHMQTT_Translation_SetDriverValue(translation, sDriverValue);
|
|
return translation;
|
|
}
|
|
else {
|
|
DBG_ERROR(AQH_LOGDOMAIN, "Either AqhValue or DriverValue missing in device description file");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|