From 4c44890d3cd3a74e8dc11fef7c5d7a6741d411e3 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Sat, 17 Feb 2024 17:33:09 +0100 Subject: [PATCH] Improved mqttlog daemaon: persistent registered devices. --- apps/aqhome-mqttlog/0BUILD | 2 + apps/aqhome-mqttlog/aqhome_mqtt.c | 62 ++++++- apps/aqhome-mqttlog/aqhome_mqtt.h | 6 + apps/aqhome-mqttlog/aqhome_mqtt_p.h | 3 + apps/aqhome-mqttlog/c_setdata.c | 22 +-- apps/aqhome-mqttlog/init.c | 60 +++++++ apps/aqhome-mqttlog/init.h | 3 + apps/aqhome-mqttlog/loop_ipc.c | 3 + apps/aqhome-mqttlog/main.c | 24 +++ apps/aqhome-mqttlog/types/topic.t2d | 7 + apps/aqhome-mqttlog/xmlread.c | 133 ++++++++++----- apps/aqhome-mqttlog/xmlread.h | 2 +- apps/aqhome-mqttlog/xmlwrite.c | 249 ++++++++++++++++++++++++++++ apps/aqhome-mqttlog/xmlwrite.h | 24 +++ aqhome-mqttlog.sh | 2 +- aqhome/aqhome.c | 43 +++++ aqhome/aqhome.h | 3 + devices/tasmota_plug.xml | 4 +- 18 files changed, 591 insertions(+), 61 deletions(-) create mode 100644 apps/aqhome-mqttlog/xmlwrite.c create mode 100644 apps/aqhome-mqttlog/xmlwrite.h diff --git a/apps/aqhome-mqttlog/0BUILD b/apps/aqhome-mqttlog/0BUILD index 9dc0c92..ac1b84f 100644 --- a/apps/aqhome-mqttlog/0BUILD +++ b/apps/aqhome-mqttlog/0BUILD @@ -55,6 +55,7 @@ aqhome_mqtt.h aqhome_mqtt_p.h xmlread.h + xmlwrite.h c_setdata.h @@ -68,6 +69,7 @@ loop_mqtt.c main.c xmlread.c + xmlwrite.c c_setdata.c diff --git a/apps/aqhome-mqttlog/aqhome_mqtt.c b/apps/aqhome-mqttlog/aqhome_mqtt.c index dfbe1ea..13ffaf5 100644 --- a/apps/aqhome-mqttlog/aqhome_mqtt.c +++ b/apps/aqhome-mqttlog/aqhome_mqtt.c @@ -95,6 +95,23 @@ int AqHomeMqtt_GetTimeout(const AQHOME_MQTT *aqh) +const char *AqHomeMqtt_GetDeviceFile(const AQHOME_MQTT *aqh) +{ + return aqh?aqh->deviceFile:NULL; +} + + + +void AqHomeMqtt_SetDeviceFile(AQHOME_MQTT *aqh, const char *s) +{ + if (aqh) { + free(aqh->deviceFile); + aqh->deviceFile=s?strdup(s):NULL; + } +} + + + AQHMQTT_DEVICE_LIST *AqHomeMqtt_GetAvailableDeviceList(const AQHOME_MQTT *aqh) { return aqh?aqh->availableDeviceList:NULL; @@ -112,16 +129,59 @@ void AqHomeMqtt_SetAvailableDeviceList(AQHOME_MQTT *aqh, AQHMQTT_DEVICE_LIST *dl +void AqHomeMqtt_SetRegisteredDeviceList(AQHOME_MQTT *aqh, AQHMQTT_DEVICE_LIST *dl) +{ + if (aqh) { + AQHMQTT_Device_List_free(aqh->registeredDeviceList); + aqh->registeredDeviceList=dl; + } +} + + + AQHMQTT_DEVICE *AqHomeMqtt_FindRegisteredDevice(AQHOME_MQTT *aqh, const char *wantedDeviceId) { - if (aqh && aqh->registeredDeviceList) + if (aqh && aqh->registeredDeviceList) { return AQHMQTT_Device_List_GetById(aqh->registeredDeviceList, wantedDeviceId); + } + else { + DBG_ERROR(NULL, "No registered devices"); + } return NULL; } +void AqHomeMqtt_DumpRegisteredDevices(const AQHOME_MQTT *aqh) +{ + if (aqh && aqh->registeredDeviceList) { + AQHMQTT_DEVICE *device; + + device=AQHMQTT_Device_List_First(aqh->registeredDeviceList); + if (device) { + fprintf(stderr, "Registered Devices:\n"); + while(device) { + const char *sDeviceName; + const char *sDeviceId; + + sDeviceName=AQHMQTT_Device_GetName(device); + sDeviceId=AQHMQTT_Device_GetId(device); + fprintf(stderr, " %s (%s)\n", sDeviceId?sDeviceId:"", sDeviceName?sDeviceName:""); + device=AQHMQTT_Device_List_Next(device); + } + } + else { + fprintf(stderr, "No registered devices\n"); + } + } + else { + fprintf(stderr, "No registered devices\n"); + } +} + + + diff --git a/apps/aqhome-mqttlog/aqhome_mqtt.h b/apps/aqhome-mqttlog/aqhome_mqtt.h index c8257c2..106347e 100644 --- a/apps/aqhome-mqttlog/aqhome_mqtt.h +++ b/apps/aqhome-mqttlog/aqhome_mqtt.h @@ -35,11 +35,17 @@ void AqHomeMqtt_SetPidFile(AQHOME_MQTT *aqh, const char *s); int AqHomeMqtt_GetTimeout(const AQHOME_MQTT *aqh); +const char *AqHomeMqtt_GetDeviceFile(const AQHOME_MQTT *aqh); +void AqHomeMqtt_SetDeviceFile(AQHOME_MQTT *aqh, const char *s); + AQHMQTT_DEVICE_LIST *AqHomeMqtt_GetAvailableDeviceList(const AQHOME_MQTT *aqh); void AqHomeMqtt_SetAvailableDeviceList(AQHOME_MQTT *aqh, AQHMQTT_DEVICE_LIST *dl); +void AqHomeMqtt_SetRegisteredDeviceList(AQHOME_MQTT *aqh, AQHMQTT_DEVICE_LIST *dl); AQHMQTT_DEVICE *AqHomeMqtt_FindRegisteredDevice(AQHOME_MQTT *aqh, const char *wantedDeviceId); +void AqHomeMqtt_DumpRegisteredDevices(const AQHOME_MQTT *aqh); + #endif diff --git a/apps/aqhome-mqttlog/aqhome_mqtt_p.h b/apps/aqhome-mqttlog/aqhome_mqtt_p.h index e9b364b..bcf2788 100644 --- a/apps/aqhome-mqttlog/aqhome_mqtt_p.h +++ b/apps/aqhome-mqttlog/aqhome_mqtt_p.h @@ -17,6 +17,7 @@ #define AQHOME_MQTT_DEFAULT_PIDFILE "/var/run/aqhome-mqtt.pid" #define AQHOME_MQTT_DEFAULT_DATADIR "/var/lib/aqhome-mqtt" +#define AQHOME_MQTT_DEFAULT_DEVICEFILE "mqttlog/registereddevices.xml" #define AQHOME_MQTT_DEFAULT_BROKER_PORT 1899 #define AQHOME_MQTT_DEFAULT_BROKER_CLIENTID "mqtt" @@ -35,6 +36,8 @@ struct AQHOME_MQTT { AQHMQTT_DEVICE_LIST *availableDeviceList; AQHMQTT_DEVICE_LIST *registeredDeviceList; + + char *deviceFile; }; diff --git a/apps/aqhome-mqttlog/c_setdata.c b/apps/aqhome-mqttlog/c_setdata.c index 50bb13c..3cc6938 100644 --- a/apps/aqhome-mqttlog/c_setdata.c +++ b/apps/aqhome-mqttlog/c_setdata.c @@ -40,30 +40,33 @@ void AqHomeMqttLog_HandleSetData(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_M AQH_VALUE *recvdValue; DBG_ERROR(NULL, "Received SETDATA request"); + AQH_SetDataIpcMsg_Parse(recvdMsg, 0); recvdValue=AQH_SetDataIpcMsg_ReadValue(recvdMsg); if (recvdValue) { const char *valueName; const char *deviceName; - valueName=recvdValue?AQH_Value_GetNameForSystem(recvdValue):NULL; + valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL; deviceName=recvdValue?AQH_Value_GetDeviceName(recvdValue):NULL; - if (deviceName) { + if (valueName && deviceName) { AQHMQTT_DEVICE *device; device=AqHomeMqtt_FindRegisteredDevice(aqh, deviceName); if (device) { char *valueDataFreeable; + DBG_ERROR(NULL, "Sending data to value \"%s\" of device \"%s\"", valueName, deviceName); valueDataFreeable=AQH_SetDataIpcMsg_ReadData(recvdMsg); _sendDataForDevice(aqh, device, valueName, valueDataFreeable); free(valueDataFreeable); } else { DBG_ERROR(NULL, "Device \"%s\" not found", deviceName); + AqHomeMqtt_DumpRegisteredDevices(aqh); } } else { - DBG_ERROR(NULL, "Request does not contain a device name"); + DBG_ERROR(NULL, "Either value name or device name missing in request"); } AQH_Value_free(recvdValue); } @@ -96,6 +99,7 @@ void _sendDataForDevice(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const ch value=valueList?AQHMQTT_Value_List_GetByName(valueList, valueName):NULL; if (value) { /* found value, create publish msg, send */ + DBG_ERROR(NULL, "Topic \"%s\" contains value \"%s\"", AQHMQTT_Topic_GetName(topic), valueName); _sendValueToMqtt(aqh, deviceId, topic, valueData); } } /* if out */ @@ -118,12 +122,12 @@ void _sendValueToMqtt(AQHOME_MQTT *aqh, const char *deviceId, const AQHMQTT_TOPI ep=AqHomeMqtt_GetMqttEndpoint(aqh); buf=_createBufferForTopic(deviceId, topic); - DBG_INFO(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:""); + DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:""); msgOut=AQH_PublishMqttMsg_new(0, 0, GWEN_Buffer_GetStart(buf), (const uint8_t*) (valueData?valueData:NULL), valueData?strlen(valueData):0); if (msgOut) { - GWEN_MsgEndpoint_AddSendMessage(ep, msgOut); + //GWEN_MsgEndpoint_AddSendMessage(ep, msgOut); } else { DBG_ERROR(NULL, "Error creating message"); @@ -140,16 +144,12 @@ GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *to buf=GWEN_Buffer_new(0, 256, 0, 1); s=AQHMQTT_Topic_GetBeforeId(topic); - if (s && *s) { + if (s && *s) GWEN_Buffer_AppendString(buf, s); - GWEN_Buffer_AppendByte(buf, '/'); - } GWEN_Buffer_AppendString(buf, deviceId); s=AQHMQTT_Topic_GetAfterId(topic); - if (s && *s) { - GWEN_Buffer_AppendByte(buf, '/'); + if (s && *s) GWEN_Buffer_AppendString(buf, s); - } return buf; } diff --git a/apps/aqhome-mqttlog/init.c b/apps/aqhome-mqttlog/init.c index a1a21ce..36a72cf 100644 --- a/apps/aqhome-mqttlog/init.c +++ b/apps/aqhome-mqttlog/init.c @@ -14,6 +14,7 @@ #include "./init.h" #include "./aqhome_mqtt_p.h" #include "./xmlread.h" +#include "./xmlwrite.h" #include "aqhome/aqhome.h" #include "aqhome/ipc/endpoint_ipc.h" @@ -87,6 +88,24 @@ int AqHomeMqtt_Init(AQHOME_MQTT *aqh, int argc, char **argv) } } + s=GWEN_DB_GetCharValue(dbArgs, "devicefile", 0, NULL); + if (s && *s) { + AqHomeMqtt_SetDeviceFile(aqh, s); + } + else { + GWEN_BUFFER *bufFilename; + + bufFilename=AQH_GetRuntimeFilePath(AQHOME_MQTT_DEFAULT_DEVICEFILE); + if (bufFilename) { + AqHomeMqtt_SetDeviceFile(aqh, GWEN_Buffer_GetStart(bufFilename)); + GWEN_Buffer_free(bufFilename); + } + else { + DBG_ERROR(NULL, "Could not setup filename for devices, please specify via command line argument"); + return GWEN_ERROR_GENERIC; + } + } + aqh->rootEndpoint=GWEN_MsgEndpoint_new("root", 0); rv=_setupMqtt(aqh, dbArgs); @@ -101,6 +120,8 @@ int AqHomeMqtt_Init(AQHOME_MQTT *aqh, int argc, char **argv) return rv; } + AqHomeMqtt_LoadRuntimeDeviceFiles(aqh); + AqHomeMqtt_ReloadDeviceFiles(aqh); return 0; @@ -112,6 +133,7 @@ void AqHomeMqtt_ReloadDeviceFiles(AQHOME_MQTT *aqh) { AQHMQTT_DEVICE_LIST *deviceList; + DBG_ERROR(NULL, "Loading devices description files"); deviceList=AqHomeMqttLog_ReadDataDeviceFiles(aqh); if (deviceList) AqHomeMqtt_SetAvailableDeviceList(aqh, deviceList); @@ -119,6 +141,32 @@ void AqHomeMqtt_ReloadDeviceFiles(AQHOME_MQTT *aqh) +void AqHomeMqtt_LoadRuntimeDeviceFiles(AQHOME_MQTT *aqh) +{ + AQHMQTT_DEVICE_LIST *deviceList; + + DBG_ERROR(NULL, "Loading registered devices from file \"%s\"", aqh->deviceFile); + deviceList=AqHomeMqttLog_ReadDeviceFile(aqh, aqh->deviceFile); + if (deviceList) + AqHomeMqtt_SetRegisteredDeviceList(aqh, deviceList); +} + + + +int AqHomeMqtt_SaveRuntimeDeviceFiles(AQHOME_MQTT *aqh) +{ + int rv; + + rv=AqHomeMqttLog_WriteDevicesFile(aqh, aqh->registeredDeviceList, aqh->deviceFile); + if (rv<0) { + DBG_INFO(NULL, "Error writing devices to \"%s\" (%d)", aqh->deviceFile, rv); + return rv; + } + return 0; +} + + + int _createPidFile(const char *pidFilename) { FILE *f; @@ -364,6 +412,18 @@ int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) I18S("Specify timeout in second (default: no timeout)"), I18S("Specify timeout in second (default: no timeout)") }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "devicefile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "d", /* short option */ + "devicefile", /* long option */ + I18S("Specify the device file"), + I18S("Specify the device file") + }, + { GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ GWEN_ArgsType_Int, /* type */ diff --git a/apps/aqhome-mqttlog/init.h b/apps/aqhome-mqttlog/init.h index d094e40..ea91e13 100644 --- a/apps/aqhome-mqttlog/init.h +++ b/apps/aqhome-mqttlog/init.h @@ -17,6 +17,9 @@ int AqHomeMqtt_Init(AQHOME_MQTT *aqh, int argc, char **argv); void AqHomeMqtt_ReloadDeviceFiles(AQHOME_MQTT *aqh); +void AqHomeMqtt_LoadRuntimeDeviceFiles(AQHOME_MQTT *aqh); +int AqHomeMqtt_SaveRuntimeDeviceFiles(AQHOME_MQTT *aqh); + #endif diff --git a/apps/aqhome-mqttlog/loop_ipc.c b/apps/aqhome-mqttlog/loop_ipc.c index a8fddcb..852c185 100644 --- a/apps/aqhome-mqttlog/loop_ipc.c +++ b/apps/aqhome-mqttlog/loop_ipc.c @@ -70,6 +70,9 @@ void _handleIpcMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg) default: break; } } + else if (protoId==0 && code==AQH_MSGTYPE_IPC_DATA_RESULT) { + /* result received */ + } else { DBG_ERROR(NULL, "Invalid IPC protocol %d (%02x)", protoId, protoId); } diff --git a/apps/aqhome-mqttlog/main.c b/apps/aqhome-mqttlog/main.c index 7dcb9ea..f0360b4 100644 --- a/apps/aqhome-mqttlog/main.c +++ b/apps/aqhome-mqttlog/main.c @@ -48,6 +48,7 @@ #define I18S(msg) msg #define AQHOME_MQTTLOG_PING_INTERVAL 120 +#define AQHOME_MQTTLOG_SAVE_INTERVAL 60 #define FULL_DEBUG @@ -143,9 +144,11 @@ void _serve(AQHOME_MQTT *aqh) int timeout; time_t startTime; time_t lastPingSendTime; + time_t lastSaveTime; GWEN_DB_NODE *dbArgs; startTime=time(NULL); + lastSaveTime=time(NULL); dbArgs=AqHomeMqtt_GetDbArgs(aqh); @@ -184,7 +187,28 @@ void _serve(AQHOME_MQTT *aqh) lastPingSendTime=time(NULL); } } + + if (1){ + time_t now; + + now=time(NULL); + if (now-lastSaveTime>AQHOME_MQTTLOG_SAVE_INTERVAL) { + DBG_ERROR(NULL, "Writing device files"); + rv=AqHomeMqtt_SaveRuntimeDeviceFiles(aqh); + if (rv<0) { + DBG_INFO(NULL, "Error writing runtime data"); + } + lastSaveTime=time(NULL); + } + } + + } /* while */ + + rv=AqHomeMqtt_SaveRuntimeDeviceFiles(aqh); + if (rv<0) { + DBG_INFO(NULL, "Error writing runtime data"); } + } diff --git a/apps/aqhome-mqttlog/types/topic.t2d b/apps/aqhome-mqttlog/types/topic.t2d index 7a8351c..8de3ccd 100644 --- a/apps/aqhome-mqttlog/types/topic.t2d +++ b/apps/aqhome-mqttlog/types/topic.t2d @@ -69,6 +69,13 @@ + + NULL + NULL + public + own + + NULL NULL diff --git a/apps/aqhome-mqttlog/xmlread.c b/apps/aqhome-mqttlog/xmlread.c index f8d75fb..dcc71b4 100644 --- a/apps/aqhome-mqttlog/xmlread.c +++ b/apps/aqhome-mqttlog/xmlread.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -41,7 +42,8 @@ */ static AQHMQTT_DEVICE_LIST *_readDeviceFiles(AQHOME_MQTT *aqh, const GWEN_STRINGLIST *sl); -static AQHMQTT_DEVICE *_readDeviceFile(AQHOME_MQTT *aqh, const char *sFilename); +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); @@ -58,27 +60,32 @@ static AQHMQTT_TRANSLATION *_readXmlTranslation(AQHOME_MQTT *aqh, GWEN_XMLNODE * * ------------------------------------------------------------------------------------------------ */ - -AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadRuntimeDataDeviceFiles(AQHOME_MQTT *aqh) +AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDeviceFile(AQHOME_MQTT *aqh, const char *sFilename) { - GWEN_STRINGLIST *sl; + int rv; - sl=AQH_GetListOfMatchingRuntimeDataFiles("aqhome/devices", "*.xml"); - if (sl) { + 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=_readDeviceFiles(aqh, sl); - GWEN_StringList_free(sl); - if (deviceList==NULL) { - DBG_INFO(NULL, "Error reading sysconf device files"); + 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; } - else { - DBG_INFO(NULL, "No sysconf device files"); - return NULL; - } } @@ -119,12 +126,12 @@ AQHMQTT_DEVICE_LIST *_readDeviceFiles(AQHOME_MQTT *aqh, const GWEN_STRINGLIST *s s=GWEN_StringListEntry_Data(se); if (s && *s) { - AQHMQTT_DEVICE *device; + int rv; - device=_readDeviceFile(aqh, s); - if (device) { - DBG_ERROR(NULL, "Adding device \"%s\" to list", AQHMQTT_Device_GetName(device)); - AQHMQTT_Device_List_Add(device, deviceList); + 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); @@ -140,11 +147,10 @@ AQHMQTT_DEVICE_LIST *_readDeviceFiles(AQHOME_MQTT *aqh, const GWEN_STRINGLIST *s - -AQHMQTT_DEVICE *_readDeviceFile(AQHOME_MQTT *aqh, const char *sFilename) +int _readDeviceFileToList(AQHOME_MQTT *aqh, const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList) { GWEN_XMLNODE *rootNode; - GWEN_XMLNODE *deviceNode; + GWEN_XMLNODE *deviceListNode; int rv; rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL); @@ -152,36 +158,72 @@ AQHMQTT_DEVICE *_readDeviceFile(AQHOME_MQTT *aqh, const char *sFilename) if (rv<0) { DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv); GWEN_XMLNode_free(rootNode); - return NULL; + return rv; } - deviceNode=GWEN_XMLNode_FindFirstTag(rootNode, "device", NULL, NULL); - if (deviceNode) { - const char *driverName; - driverName=GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL); - if (driverName && *driverName && strcasecmp(driverName, "mqtt")==0) { - AQHMQTT_DEVICE *device; + deviceListNode=GWEN_XMLNode_FindFirstTag(rootNode, "devices", NULL, NULL); + if (deviceListNode==NULL) + deviceListNode=rootNode; - device=_readXmlDevice(aqh, deviceNode); - GWEN_XMLNode_free(rootNode); - if (device==NULL) { - DBG_INFO(NULL, "Error reading device from XML file \"%s\" (%d)", sFilename, rv); - return NULL; - } - return device; - } - else { - DBG_INFO(NULL, "Device is not an MQTT device, ignoring"); - } - } - else { - DBG_INFO(NULL, "XML file \"%s\" does not contain a element", sFilename); + 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 NULL; + return rv; } GWEN_XMLNode_free(rootNode); - return NULL; + 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; + } } @@ -274,6 +316,7 @@ AQHMQTT_TOPIC *_readXmlTopic(AQHOME_MQTT *aqh, GWEN_XMLNODE *topicNode) } 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)); diff --git a/apps/aqhome-mqttlog/xmlread.h b/apps/aqhome-mqttlog/xmlread.h index 6bbae93..e3a1772 100644 --- a/apps/aqhome-mqttlog/xmlread.h +++ b/apps/aqhome-mqttlog/xmlread.h @@ -16,7 +16,7 @@ AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDataDeviceFiles(AQHOME_MQTT *aqh); -AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadRuntimeDataDeviceFiles(AQHOME_MQTT *aqh); +AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDeviceFile(AQHOME_MQTT *aqh, const char *sFilename); diff --git a/apps/aqhome-mqttlog/xmlwrite.c b/apps/aqhome-mqttlog/xmlwrite.c new file mode 100644 index 0000000..aee0968 --- /dev/null +++ b/apps/aqhome-mqttlog/xmlwrite.c @@ -0,0 +1,249 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 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 "./xmlwrite.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 + +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _writeDevicesToXml(const AQHMQTT_DEVICE_LIST *deviceList, GWEN_XMLNODE *node); +static void _writeDeviceToXml(const AQHMQTT_DEVICE *device, GWEN_XMLNODE *node); +static void _writeTopicToXml(const AQHMQTT_TOPIC *topic, GWEN_XMLNODE *node); +static void _writeValueToXml(const AQHMQTT_VALUE *value, GWEN_XMLNODE *node); +static void _writeTranslationToXml(const AQHMQTT_TRANSLATION *t, GWEN_XMLNODE *nTranslation); +static void _setXmlPropertyIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value); +static void _setXmlCharValueIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + + + +int AqHomeMqttLog_WriteDevicesFile(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE_LIST *deviceList, const char *sFilename) +{ + int rv; + + rv=GWEN_Directory_GetPath(sFilename, GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_VARIABLE); + if (rv<0) { + DBG_ERROR(NULL, "Could not access file \"%s\"", sFilename); + return GWEN_ERROR_GENERIC; + } + else { + GWEN_XMLNODE *rootNode; + GWEN_BUFFER *nbuf; + + rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "root"); + _writeDevicesToXml(deviceList, rootNode); + + nbuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(nbuf, sFilename); + GWEN_Buffer_AppendString(nbuf, ".tmp"); + unlink(GWEN_Buffer_GetStart(nbuf)); + rv=GWEN_XMLNode_WriteFile(rootNode, + GWEN_Buffer_GetStart(nbuf), + GWEN_XML_FLAGS_SIMPLE | GWEN_XML_FLAGS_HANDLE_HEADERS | GWEN_XML_FLAGS_INDENT); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error writing XML file \"%s\": %d", GWEN_Buffer_GetStart(nbuf), rv); + GWEN_Buffer_free(nbuf); + GWEN_XMLNode_free(rootNode); + return rv; + } + if (rename(GWEN_Buffer_GetStart(nbuf), sFilename)<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error renaming \"%s\"->\"%s\": %d (%s)", + GWEN_Buffer_GetStart(nbuf), sFilename, errno, strerror(errno)); + GWEN_Buffer_free(nbuf); + GWEN_XMLNode_free(rootNode); + return rv; + } + GWEN_Buffer_free(nbuf); + GWEN_XMLNode_free(rootNode); + return 0; + } +} + + + +void _writeDevicesToXml(const AQHMQTT_DEVICE_LIST *deviceList, GWEN_XMLNODE *node) +{ + if (deviceList && AQHMQTT_Device_List_GetCount(deviceList)) { + GWEN_XMLNODE *nDevices; + const AQHMQTT_DEVICE *device; + + nDevices=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "devices"); + device=AQHMQTT_Device_List_First(deviceList); + while(device) { + GWEN_XMLNODE *nDevice; + + nDevice=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "device"); + _writeDeviceToXml(device, nDevice); + GWEN_XMLNode_AddChild(nDevices, nDevice); + + device=AQHMQTT_Device_List_Next(device); + } + + GWEN_XMLNode_AddChild(node, nDevices); + } +} + + +void _writeDeviceToXml(const AQHMQTT_DEVICE *device, GWEN_XMLNODE *node) +{ + const AQHMQTT_TOPIC_LIST *topicList; + + _setXmlPropertyIfNotNull(node, "id", AQHMQTT_Device_GetId(device)); + _setXmlPropertyIfNotNull(node, "name", AQHMQTT_Device_GetName(device)); + _setXmlPropertyIfNotNull(node, "driver", AQHMQTT_Device_GetDriver(device)); + + topicList=AQHMQTT_Device_GetTopicList(device); + if (topicList && AQHMQTT_Topic_List_GetCount(topicList)) { + GWEN_XMLNODE *nTopics; + const AQHMQTT_TOPIC *topic; + + nTopics=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "mqtttopics"); + topic=AQHMQTT_Topic_List_First(topicList); + while(topic) { + GWEN_XMLNODE *nTopic; + + nTopic=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "mqtttopic"); + _writeTopicToXml(topic, nTopic); + GWEN_XMLNode_AddChild(nTopics, nTopic); + + topic=AQHMQTT_Topic_List_Next(topic); + } + + GWEN_XMLNode_AddChild(node, nTopics); + } +} + + + + +void _writeTopicToXml(const AQHMQTT_TOPIC *topic, GWEN_XMLNODE *node) +{ + const AQHMQTT_VALUE_LIST *valueList; + + GWEN_XMLNode_SetProperty(node, "type", AQHMQTT_TopicType_toString(AQHMQTT_Topic_GetTopicType(topic))); + GWEN_XMLNode_SetProperty(node, "direction", AQHMQTT_TopicDir_toString(AQHMQTT_Topic_GetDirection(topic))); + _setXmlPropertyIfNotNull(node, "name", AQHMQTT_Topic_GetName(topic)); + _setXmlCharValueIfNotNull(node, "topic", AQHMQTT_Topic_GetTopic(topic)); + _setXmlCharValueIfNotNull(node, "mask", AQHMQTT_Topic_GetMask(topic)); + _setXmlCharValueIfNotNull(node, "beforeId", AQHMQTT_Topic_GetBeforeId(topic)); + _setXmlCharValueIfNotNull(node, "afterId", AQHMQTT_Topic_GetAfterId(topic)); + + valueList=AQHMQTT_Topic_GetValueList(topic); + if (valueList && AQHMQTT_Value_List_GetCount(valueList)) { + GWEN_XMLNODE *nValues; + const AQHMQTT_VALUE *value; + + nValues=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "values"); + value=AQHMQTT_Value_List_First(valueList); + while(value) { + GWEN_XMLNODE *nValue; + + nValue=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "value"); + _writeValueToXml(value, nValue); + GWEN_XMLNode_AddChild(nValues, nValue); + + value=AQHMQTT_Value_List_Next(value); + } + + GWEN_XMLNode_AddChild(node, nValues); + } +} + + + + +void _writeValueToXml(const AQHMQTT_VALUE *value, GWEN_XMLNODE *node) +{ + const AQHMQTT_TRANSLATION_LIST *translationList; + + _setXmlPropertyIfNotNull(node, "name", AQHMQTT_Value_GetName(value)); + _setXmlPropertyIfNotNull(node, "units", AQHMQTT_Value_GetValueUnits(value)); + _setXmlPropertyIfNotNull(node, "path", AQHMQTT_Value_GetPath(value)); + GWEN_XMLNode_SetProperty(node, "type", AQHMQTT_ValueType_toString(AQHMQTT_Value_GetValueType(value))); + + translationList=AQHMQTT_Value_GetTranslationList(value); + if (translationList && AQHMQTT_Translation_List_GetCount(translationList)) { + GWEN_XMLNODE *nTranslations; + const AQHMQTT_TRANSLATION *translation; + + nTranslations=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "translations"); + translation=AQHMQTT_Translation_List_First(translationList); + while(translation) { + GWEN_XMLNODE *nTranslation; + + nTranslation=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, "transaction"); + _writeTranslationToXml(translation, nTranslation); + GWEN_XMLNode_AddChild(nTranslations, nTranslation); + + translation=AQHMQTT_Translation_List_Next(translation); + } + + GWEN_XMLNode_AddChild(node, nTranslations); + } +} + + + +void _writeTranslationToXml(const AQHMQTT_TRANSLATION *t, GWEN_XMLNODE *nTranslation) +{ + _setXmlPropertyIfNotNull(nTranslation, "aqhValue", AQHMQTT_Translation_GetAqhValue(t)); + _setXmlPropertyIfNotNull(nTranslation, "driverValue", AQHMQTT_Translation_GetDriverValue(t)); +} + + + +void _setXmlPropertyIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value) +{ + if (value && *value) + GWEN_XMLNode_SetProperty(n, name, value); +} + + + +void _setXmlCharValueIfNotNull(GWEN_XMLNODE *n, const char *name, const char *value) +{ + if (value && *value) + GWEN_XMLNode_SetCharValue(n, name, value); +} + + + + + diff --git a/apps/aqhome-mqttlog/xmlwrite.h b/apps/aqhome-mqttlog/xmlwrite.h new file mode 100644 index 0000000..f814ba1 --- /dev/null +++ b/apps/aqhome-mqttlog/xmlwrite.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 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. + ****************************************************************************/ + +#ifndef AQHOME_MQTTLOG_XMLWRITE_H +#define AQHOME_MQTTLOG_XMLWRITE_H + + +#include "aqhome-mqttlog/aqhome_mqtt.h" +#include "aqhome-mqttlog/types/device.h" + + + +int AqHomeMqttLog_WriteDevicesFile(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE_LIST *deviceList, const char *sFilename); + + + +#endif + + diff --git a/aqhome-mqttlog.sh b/aqhome-mqttlog.sh index 64b150a..7219f2f 100755 --- a/aqhome-mqttlog.sh +++ b/aqhome-mqttlog.sh @@ -4,5 +4,5 @@ export AQHOME_LOGLEVEL=info export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" # 0-build/apps/aqhome-mqttlog/aqhome-mqttlog -ma 192.168.117.192 -mp 1883 -W /tmp/aqhome/mqttlog -i apps/aqhome-mqttlog/mqttlog.conf --mqttclientid=AQHOMEMQTTLOGTEST $* -0-build/apps/aqhome-mqttlog/aqhome-mqttlog --mqttclientid=AQHOMEMQTTLOGTEST -p ./aqhome-mqtt.pid "$@" +0-build/apps/aqhome-mqttlog/aqhome-mqttlog --mqttclientid=AQHOMEMQTTLOGTEST -p ./aqhome-mqtt.pid -d ./aqhome-mqtt.devices "$@" diff --git a/aqhome/aqhome.c b/aqhome/aqhome.c index 1832f82..bba13cd 100644 --- a/aqhome/aqhome.c +++ b/aqhome/aqhome.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -39,6 +40,7 @@ static void _finiPathManager(void); static void _initI18n(void); static void _definePath(const char *pathName, const char *pathValue); static GWEN_STRINGLIST *_getListOfMatchingFiles(const char *pathName, const char *subFolder, const char *mask); +static GWEN_BUFFER *_getRuntimeFilePath(const char *pathName, const char *sFilename); @@ -106,6 +108,13 @@ GWEN_STRINGLIST *AQH_GetListOfMatchingSysconfFiles(const char *subFolder, const +GWEN_BUFFER *AQH_GetRuntimeFilePath(const char *sFilename) +{ + return _getRuntimeFilePath(AQHOME_PM_RTDATADIR, sFilename); +} + + + GWEN_DB_NODE *AQH_LoadConfigFile(void) { @@ -263,3 +272,37 @@ GWEN_STRINGLIST *_getListOfMatchingFiles(const char *pathName, const char *subFo +GWEN_BUFFER *_getRuntimeFilePath(const char *pathName, const char *sFilename) +{ + GWEN_STRINGLIST *sl; + + sl=GWEN_PathManager_GetPaths(AQHOME_PM_LIBNAME, pathName); + if (sl) { + GWEN_STRINGLISTENTRY *se; + + se=GWEN_StringList_FirstEntry(sl); + if (se) { + const char *s; + + s=GWEN_StringListEntry_Data(se); + if (s && *s) { + GWEN_BUFFER *buf; + + buf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(buf, s); + if (sFilename && *sFilename) { + GWEN_Buffer_AppendByte(buf, GWEN_DIR_SEPARATOR); + GWEN_Buffer_AppendString(buf, sFilename); + } + GWEN_StringList_free(sl); + return buf; + } + } + GWEN_StringList_free(sl); + } + + return NULL; +} + + + diff --git a/aqhome/aqhome.h b/aqhome/aqhome.h index 0438dec..99f9c60 100644 --- a/aqhome/aqhome.h +++ b/aqhome/aqhome.h @@ -14,6 +14,7 @@ #include #include +#include @@ -27,6 +28,8 @@ AQHOME_API GWEN_STRINGLIST *AQH_GetListOfMatchingDataFiles(const char *subFolder AQHOME_API GWEN_STRINGLIST *AQH_GetListOfMatchingRuntimeDataFiles(const char *subFolder, const char *mask); AQHOME_API GWEN_STRINGLIST *AQH_GetListOfMatchingSysconfFiles(const char *subFolder, const char *mask); +AQHOME_API GWEN_BUFFER *AQH_GetRuntimeFilePath(const char *sFilename); + AQHOME_API GWEN_STRINGLIST *AQH_GetGlobalDataDirs(void); AQHOME_API GWEN_STRINGLIST *AQH_GetGlobalSysconfDirs(void); diff --git a/devices/tasmota_plug.xml b/devices/tasmota_plug.xml index e4d5bab..9e8527f 100644 --- a/devices/tasmota_plug.xml +++ b/devices/tasmota_plug.xml @@ -3,7 +3,7 @@ - + tele/tasmota/*/SENSOR tele/tasmota/ /SENSOR @@ -20,7 +20,7 @@ - + cmnd/tasmota/ /Power