Improved mqttlog daemaon: persistent registered devices.

This commit is contained in:
Martin Preuss
2024-02-17 17:33:09 +01:00
parent ef22bd65ea
commit 4c44890d3c
18 changed files with 591 additions and 61 deletions

View File

@@ -55,6 +55,7 @@
aqhome_mqtt.h
aqhome_mqtt_p.h
xmlread.h
xmlwrite.h
c_setdata.h
</headers>
@@ -68,6 +69,7 @@
loop_mqtt.c
main.c
xmlread.c
xmlwrite.c
c_setdata.c
</sources>

View File

@@ -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:"<no id>", sDeviceName?sDeviceName:"<no name>");
device=AQHMQTT_Device_List_Next(device);
}
}
else {
fprintf(stderr, "No registered devices\n");
}
}
else {
fprintf(stderr, "No registered devices\n");
}
}

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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:"<empty>");
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
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;
}

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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");
}
}

View File

@@ -69,6 +69,13 @@
<flags></flags>
</member>
<member name="name" type="char_ptr" maxlen="128">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="mask" type="char_ptr" maxlen="128">
<default>NULL</default>
<preset>NULL</preset>

View File

@@ -22,6 +22,7 @@
#include <gwenhywfar/endpoint_multilayer.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/xml.h>
#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>
@@ -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 <device> 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:"<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;
}
}
@@ -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));

View File

@@ -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);

View File

@@ -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 <config.h>
#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 <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>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
/* ------------------------------------------------------------------------------------------------
* 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);
}

View File

@@ -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

View File

@@ -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 "$@"

View File

@@ -17,6 +17,7 @@
#include <gwenhywfar/pathmanager.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/stringlist.h>
#include <stdlib.h>
@@ -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;
}

View File

@@ -14,6 +14,7 @@
#include <gwenhywfar/stringlist.h>
#include <gwenhywfar/db.h>
#include <gwenhywfar/buffer.h>
@@ -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);

View File

@@ -3,7 +3,7 @@
<device name="tasmotaplug" driver="mqtt">
<mqtttopics>
<mqtttopic type="json" direction="in" >
<mqtttopic type="json" name="sensor" direction="in" >
<mask>tele/tasmota/*/SENSOR</mask>
<beforeId>tele/tasmota/</beforeId>
<afterId>/SENSOR</afterId>
@@ -20,7 +20,7 @@
</mqtttopic>
<mqtttopic type="num" direction="out" >
<mqtttopic type="num" name="power" direction="out" >
<beforeId>cmnd/tasmota/</beforeId>
<afterId>/Power</afterId>