/**************************************************************************** * 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 #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 #include #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * 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", "*.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; int rv; 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 (%d)", rv); 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:""); } else { DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:""); } 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 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 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 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:""); 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:""); 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 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 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:""); 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 element"); } return value; } AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(AQHOME_MQTT *aqh, GWEN_XMLNODE *parentNode) { AQHMQTT_TRANSLATION_LIST *translationList; GWEN_XMLNODE *translationNode; translationList=AQHMQTT_Value_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 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; } }