407 Commits

Author SHA1 Message Date
Martin Preuss
aca24183e4 avr: added defs for uart module. 2024-10-20 18:55:11 +02:00
Martin Preuss
1626526714 avr: add PIR pin. 2024-10-20 18:54:37 +02:00
Martin Preuss
7ad64cd654 devices: write denoms into XML files. 2024-10-20 18:54:14 +02:00
Martin Preuss
cc6ed1198b avr: beautification. 2024-10-20 18:53:50 +02:00
Martin Preuss
fae5b55e82 aqhome-nodes: introduced DENOM to value (not sure whether this is needed). 2024-10-20 18:53:30 +02:00
Martin Preuss
a6af10a32d avr: added routine Util_WaitForPinState1ms 2024-10-20 18:50:46 +02:00
Martin Preuss
97138bcbe0 avr: fixed a typo. 2024-10-20 18:49:53 +02:00
Martin Preuss
2b8cabd391 avr: add code to prepare a RESULT message. 2024-10-20 18:49:39 +02:00
Martin Preuss
85d0992ce9 avr: added uart_bitbang module.
Started reorganizing COM module by splitting into higher and lower level
functions.
2024-10-20 18:49:12 +02:00
Martin Preuss
b3199681de avr: optimized value functions. 2024-10-20 18:46:34 +02:00
Martin Preuss
b78fc0999b avr: added message, fixed description. 2024-10-20 18:40:50 +02:00
Martin Preuss
3cba4a1f7c avr: make COM2 buffer functions macros. 2024-10-20 18:40:24 +02:00
Martin Preuss
d7705590fe avr: make REED module usable with one and two output pins. 2024-10-06 01:31:44 +02:00
Martin Preuss
adcb037976 avr: fixed 1-wire protocol for multiple speeds.
fixed delays won't work with 8MHz when calibrated for 1MHz... use
wait macro instead.
2024-10-06 01:30:52 +02:00
Martin Preuss
072ed88102 only announce values when there is a valid uid. 2024-10-02 22:10:31 +02:00
Martin Preuss
cc1dcc4d7f adapted to last changes in gwen. 2024-10-02 22:09:43 +02:00
Martin Preuss
25efeeb244 decreased verbosity. 2024-10-02 22:08:16 +02:00
Martin Preuss
0926ba2381 aqhome-tool: use refMsgId when waiting for response. 2024-10-01 23:45:15 +02:00
Martin Preuss
1d08945ae5 aqhome-tool: rewrote SETDATA command. 2024-10-01 22:35:12 +02:00
Martin Preuss
d2ac801223 aqhome-tool: add Utils_WaitForResponse() 2024-10-01 22:34:55 +02:00
Martin Preuss
8f6291ef02 aqhome-nodes: removed unneeded include. 2024-10-01 22:05:52 +02:00
Martin Preuss
21d47b8b7f aqhome-nodes: use request functions from aqhome/ipc. 2024-10-01 22:04:42 +02:00
Martin Preuss
6f5da8ee6c aqhome-data: use requests for SETDATA ipc command. 2024-10-01 22:04:01 +02:00
Martin Preuss
9c2001285b aqhome: add requests functions. 2024-10-01 22:03:32 +02:00
Martin Preuss
8199f7c3b0 aqhome-nodes: re-implemented setdata request received via broker. 2024-09-30 22:43:35 +02:00
Martin Preuss
03f9178dd2 Revert "Revert "aqhome: convenience code.""
This reverts commit bb77c6acd1.
2024-09-30 18:28:38 +02:00
Martin Preuss
0566483575 avr: removed values from device description file for n14. 2024-09-30 18:28:01 +02:00
Martin Preuss
3a9bb3d5ec aqhome-nodes: only announce values if uid != 0 and != -1
Those ids occur for freshly assembled nodes.
2024-09-30 18:26:05 +02:00
Martin Preuss
2d7459394e added notes about fuses. 2024-09-29 21:12:08 +02:00
Martin Preuss
bb77c6acd1 Revert "aqhome: convenience code."
This reverts commit 10564ec78f.
2024-09-29 21:09:20 +02:00
Martin Preuss
10564ec78f aqhome: convenience code. 2024-09-29 21:09:08 +02:00
Martin Preuss
ee73bb0f69 avr: added screen.asm files to buildfiles. 2024-09-29 21:03:40 +02:00
Martin Preuss
8aa4143d8a avr: move ds18b20 screen code into module file. 2024-09-29 21:03:12 +02:00
Martin Preuss
c22edcbbec avr: enable owi and ds18b20 module. 2024-09-29 21:02:34 +02:00
Martin Preuss
8dd9e69bdf aqhome: fixed PING ipc request. 2024-09-29 19:03:54 +02:00
Martin Preuss
2014304c73 aqhome: added defs for devices n06 and n12. 2024-09-29 15:09:05 +02:00
Martin Preuss
c2d7564e0c avr: removed debug vars from CPRO module. 2024-09-29 15:08:33 +02:00
Martin Preuss
d70c673b8a avr: introduced screens. 2024-09-29 15:05:33 +02:00
Martin Preuss
23489dd5bf avr: removed unneeded code. 2024-09-29 15:03:36 +02:00
Martin Preuss
5767c1307d avr: improved protocol timing for COM2.
- increased waiting time after lowering ATTN line
- lengthten stop bit for cleaner frames (now recognized by PulseView)
- count "NOTFORME" conditions
- introduce definition COM_HALFBIT_LENGTH (I dont' trust value calculations
  in avrasm)
2024-09-29 14:59:19 +02:00
Martin Preuss
b93ead5e5f avr: fixed a typo. 2024-09-27 01:23:27 +02:00
Martin Preuss
6fc7f5fc7a avr: fixed clobber lists. 2024-09-27 01:23:16 +02:00
Martin Preuss
60c86c7598 Revert "avr: Try to fix a bug (not respecting DENY_ADDRESS)."
This reverts commit 07f99b7050.
2024-09-27 00:33:26 +02:00
Martin Preuss
07f99b7050 avr: Try to fix a bug (not respecting DENY_ADDRESS). 2024-09-27 00:33:11 +02:00
Martin Preuss
6676024cb0 aqhome: added "modality" to values. 2024-09-26 21:12:42 +02:00
Martin Preuss
cf9118e41d aqhome-nodes: started using requests. 2024-09-26 21:12:26 +02:00
Martin Preuss
49d037c040 more work on data and nodes service. 2024-09-26 21:11:33 +02:00
Martin Preuss
b0b6efb1c3 adapted to latest changes in gwen, more work on data and nodes servers. 2024-09-26 10:45:22 +02:00
Martin Preuss
be053b035f aqhome-react: enable setvalue 2024-09-22 21:25:45 +02:00
Martin Preuss
a839d97140 aqhome-tool: added command "setnodevalue". 2024-09-22 21:25:05 +02:00
Martin Preuss
bdcbaa2b46 aqhome-nodes: improved handling of VALUE3 messages. 2024-09-22 21:24:34 +02:00
Martin Preuss
a624331166 aqhome: add constructor for VALUE3 messages. 2024-09-22 21:23:53 +02:00
Martin Preuss
7e4977f472 avr: fixed a typo. 2024-09-22 21:22:56 +02:00
Martin Preuss
9adb95d532 avr: make basetimer work on AtTiny85, too. 2024-09-22 21:22:46 +02:00
Martin Preuss
f729766e6b avr: added device n14 (led strip controller @ 8MHz). 2024-09-22 21:21:46 +02:00
Martin Preuss
4548b3c225 aqhome: discard data on bad messages, make socket non-blocking.
still doesn't seem to recover from bad messages.
2024-09-20 01:28:54 +02:00
Martin Preuss
caa7138738 avr: disabled debug led. 2024-09-18 20:24:48 +02:00
Martin Preuss
91752156f2 avr: fixed timer usage in COM2 and COM2_PROTO module. 2024-09-18 20:24:32 +02:00
Martin Preuss
a1a64fec42 avr: use sk6812 in n12 device (test case). 2024-09-18 20:23:59 +02:00
Martin Preuss
9e80636acb avr: enable sk6812 module. 2024-09-18 20:23:16 +02:00
Martin Preuss
741dd4fe12 avr: some sorting. 2024-09-18 20:22:13 +02:00
Martin Preuss
5ca1fd4275 avr: added module for sk6812 LED controllers. 2024-09-18 20:21:55 +02:00
Martin Preuss
7590aa3717 aqhome-nodes: write db on fini(). Extract info from DEVICE message. 2024-09-18 00:15:43 +02:00
Martin Preuss
08399ecf87 aqhome-nodes: decreased verbosity. 2024-09-18 00:14:42 +02:00
Martin Preuss
0e83988904 avr: fixed wait phase in 1-wire-master code. 2024-09-18 00:14:08 +02:00
Martin Preuss
3bec999ae0 avr: don't wait before checking ATTN line. 2024-09-18 00:13:37 +02:00
Martin Preuss
5b3c813981 avr: beautifications. 2024-09-18 00:12:56 +02:00
Martin Preuss
b490646799 avr: fixed docu. 2024-09-18 00:11:59 +02:00
Martin Preuss
f912c537d4 avr: use n12 as test node (using new pcb "X01") with 8MHz. 2024-09-18 00:11:47 +02:00
Martin Preuss
3af514d946 avr: add Utils_WaitFor100MicroSecs 2024-09-18 00:09:35 +02:00
Martin Preuss
9a2a1764d9 aqhome: fixed a bug. 2024-09-18 00:09:16 +02:00
Martin Preuss
4687a8726b aqhome: moved datafile implementations into their own folder. 2024-09-13 21:43:00 +02:00
Martin Preuss
7a5900be25 avr: added functions to change speed.
Main code might work at 8 MHz, but boot code is compiled for 1 MHz,
so we need to set speed accordingly when rebooting into boot loader.
2024-09-13 21:41:38 +02:00
Martin Preuss
2d09e22ec6 avr: "Utils_WaitNanoSecs 100000" doesn't work at 8MHz (too many cycles). 2024-09-13 21:40:22 +02:00
Martin Preuss
cdcb4e2b3e avr: rewrote timer code (split into timer and basetimer).
Only basetimer depends on hardware and clock speed. Works onj AtTiny 84
at 1 MHz and 8 MHz.
2024-09-13 21:39:24 +02:00
Martin Preuss
6ff68b848c avr: fixed firmware variant. 2024-09-13 01:11:33 +02:00
Martin Preuss
0f98ed87a4 aqhome: improved error handling. 2024-09-13 01:11:11 +02:00
Martin Preuss
ee94d8a583 avr: finalized label names. 2024-09-13 01:08:07 +02:00
Martin Preuss
4ba0e01c9f avr: disable DS18B20 support for devices n11 and n12. 2024-09-12 19:19:14 +02:00
Martin Preuss
c908eb4840 avr: removed dead code. 2024-09-12 13:12:01 +02:00
Martin Preuss
892dc65898 avr: added missing defs. 2024-09-12 13:11:50 +02:00
Martin Preuss
0c819bcd19 avr: added documentation about COM2 lines and protocol. 2024-09-12 13:06:35 +02:00
Martin Preuss
caf04e88f3 avr: n12: handle MODULES_DS18B20 and MODULES_OWI_MASTER 2024-09-12 13:06:10 +02:00
Martin Preuss
79f4018341 avr: send DS18B20 sensor data. 2024-09-12 13:05:27 +02:00
Martin Preuss
f1fe99673b avr: added debug instr. 2024-09-12 13:05:01 +02:00
Martin Preuss
79afc70b92 avr: include printDs inside #ifdef 2024-09-12 13:04:39 +02:00
Martin Preuss
ec033cfd10 avr: fixed crc code. 2024-09-12 13:03:45 +02:00
Martin Preuss
0107330c32 avr: use provided polynomial. 2024-09-12 11:46:44 +02:00
Martin Preuss
cb43378a2f avr: fixed typos. 2024-09-12 11:46:20 +02:00
Martin Preuss
0fafb36100 avr: added definitions to devices. 2024-09-12 11:46:02 +02:00
Martin Preuss
88aea30da1 avr: added missing subdirs. 2024-09-12 11:45:32 +02:00
Martin Preuss
4a20933397 avr: added debug output to lcd for ds18b20. 2024-09-12 11:45:22 +02:00
Martin Preuss
a10cd8293b avr: provide polynomial for crc function. 2024-09-12 11:44:54 +02:00
Martin Preuss
c055ee6cc2 avr: added module for 1-wire master role (used by DS18B20). 2024-09-12 11:44:10 +02:00
Martin Preuss
7fbe9744ea avr: fixed apidoc. 2024-09-12 11:43:11 +02:00
Martin Preuss
0fabc6d613 avr: fixed code for DS18B20 (works now!). 2024-09-12 11:42:59 +02:00
Martin Preuss
39933a957b avr: added first version of ds18b20 code. 2024-09-12 04:01:52 +02:00
Martin Preuss
6308207548 ignore coredumps. 2024-09-11 01:36:26 +02:00
Martin Preuss
2de89ecc96 avr: fixed module cny70 (basically works now). 2024-09-11 01:35:24 +02:00
Martin Preuss
85d7ccf0f2 avr: fixed port definitions for device n12. 2024-09-11 01:34:54 +02:00
Martin Preuss
d14363b8f0 avr: disable lcd module on standard n11 device. 2024-09-11 01:34:21 +02:00
Martin Preuss
a298e6845a avr: enable new devices. 2024-09-11 01:33:52 +02:00
Martin Preuss
d8985183f7 avr: add test device (n00). 2024-09-11 01:33:36 +02:00
Martin Preuss
ad34bc8ec3 avr: add olde device n06.
This one featured a cny70 sensor to be used to detect open windows.
2024-09-11 01:33:14 +02:00
Martin Preuss
0bc37343a5 avr: improved apidoc. 2024-09-10 10:58:48 +02:00
Martin Preuss
73f749b5bb Revert "avr: make lcdOneByteCommand() and lcdTwoByteCommand() more generic."
This reverts commit 28882010a9.
2024-09-10 10:25:29 +02:00
Martin Preuss
28882010a9 avr: make lcdOneByteCommand() and lcdTwoByteCommand() more generic. 2024-09-10 10:25:09 +02:00
Martin Preuss
b0afa98387 avr: change debug position. 2024-09-10 02:57:54 +02:00
Martin Preuss
6ba1a18de1 avr: enable lcd module. 2024-09-10 02:57:33 +02:00
Martin Preuss
c79037a1e1 avr: move definition of TWI_BIT_LENGTH to device defs. 2024-09-10 02:56:50 +02:00
Martin Preuss
743b33664f avr: improved lcd module. 2024-09-10 02:56:12 +02:00
Martin Preuss
fd43a89bcd avr: fixed abug. 2024-09-09 15:50:10 +02:00
Martin Preuss
9bd376464a avr: optimized cny70 module (not tested, yet). 2024-09-09 15:49:58 +02:00
Martin Preuss
e70c294d9b avr: added code for missing modules. 2024-09-09 15:49:26 +02:00
Martin Preuss
a456211438 avr: timer table is now consulted every 100ms (instead of every second). 2024-09-09 15:49:07 +02:00
Martin Preuss
1c4d94c5ce avr: fixed a typo in LED pin def. 2024-09-09 15:47:11 +02:00
Martin Preuss
50546799dc aqhome: add new msg codes to type group "VALUES". 2024-09-09 15:46:48 +02:00
Martin Preuss
9bc837249e aqhome: fixed a minor bug. 2024-09-09 15:46:08 +02:00
Martin Preuss
2215d68544 avr: only send reed message if we have an address. 2024-09-07 15:40:17 +02:00
Martin Preuss
fc6130f4cc avr: fixed a bug. 2024-09-07 15:39:54 +02:00
Martin Preuss
cf1983d264 avr: set response code. 2024-09-07 14:59:01 +02:00
Martin Preuss
ed21f4bbfc avr: fixed a bug in message creation. 2024-09-07 14:58:37 +02:00
Martin Preuss
d99787b787 avr: fixed hardware version. 2024-09-07 14:58:05 +02:00
Martin Preuss
5f2da242c5 aqhome: adapted to latest changes. 2024-09-07 14:57:40 +02:00
Martin Preuss
9b724d5a5f aqhome: adapted to latest changes in node firmware. 2024-09-06 22:52:25 +02:00
Martin Preuss
2fa3e9d4ab avr: re-introduce UID to new VALUE messages, set UID in VALUE response messages. 2024-09-06 22:50:32 +02:00
Martin Preuss
5e4ca45443 avr: updated display message. 2024-09-05 23:41:48 +02:00
Martin Preuss
090917ea09 ave: removed unneeded defs, beautifications. 2024-09-05 23:04:24 +02:00
Martin Preuss
7e3523ec42 avr: improved MESSAGES documentation. 2024-09-05 23:04:01 +02:00
Martin Preuss
12a2cc1b8e avr: fixed a typo. 2024-09-05 21:36:38 +02:00
Martin Preuss
6b0972d76e avr: implement setvalue for n12. 2024-09-05 21:36:24 +02:00
Martin Preuss
984cccc25b avr: updated documentation for messages. 2024-09-05 18:55:02 +02:00
Martin Preuss
dbad1ba8a1 avr: removed no longer needee defs. 2024-09-05 18:54:32 +02:00
Martin Preuss
b70e0e0bc2 avr: moved defs to correct files. 2024-09-05 18:54:14 +02:00
Martin Preuss
595a140428 avr: reorder code (only need to load X if there is still something to do). 2024-09-05 18:53:00 +02:00
Martin Preuss
b94105bf78 avr: refactor SEND_VALUE message code. 2024-09-05 18:52:25 +02:00
Martin Preuss
eec544d1b8 avr: send firmware version in FLASH_READY and DEVICE messages. 2024-09-05 18:50:43 +02:00
Martin Preuss
cb6e21715a avr: shorten CPRO_WriteComSendStats 2024-09-05 18:49:19 +02:00
Martin Preuss
026d4a57fb avr: remove unneeded code. 2024-09-05 18:48:27 +02:00
Martin Preuss
7745accfae avr: refactor ctc calculation code. 2024-09-05 18:48:11 +02:00
Martin Preuss
cea3137b5a avr: add LED module LED_SIMPLE
very much shorter version with only basic functionality.
2024-09-05 18:47:32 +02:00
Martin Preuss
c73fede935 avr: adapted to latest changes. 2024-09-05 03:28:05 +02:00
Martin Preuss
b56947dcba avr: removed avr targets from folder avr/ 2024-09-05 03:27:40 +02:00
Martin Preuss
a23997db39 avr: adapted README to latest changes. 2024-09-05 03:20:42 +02:00
Martin Preuss
51ba17d43f avr: fixed list of modified regs. 2024-09-05 03:20:21 +02:00
Martin Preuss
575b0285fe avr: optimize for space. 2024-09-05 03:20:00 +02:00
Martin Preuss
277a27516c avr: fixed bootloader address var (depends on device). 2024-09-05 03:19:01 +02:00
Martin Preuss
817ff958b9 avr: use common code. 2024-09-05 03:18:22 +02:00
Martin Preuss
ea2cd6e58e avr: reorganized data to simplify message creation. 2024-09-05 03:17:32 +02:00
Martin Preuss
1f2f8b574e avr: work on device n12. 2024-09-05 03:16:59 +02:00
Martin Preuss
cd6413e7a6 avr: added device n11. 2024-09-05 03:16:27 +02:00
Martin Preuss
425c26cfbb avr: added common code. 2024-09-05 03:15:51 +02:00
Martin Preuss
f2ab0d8b9f avr: fixed comments. 2024-09-04 23:18:20 +02:00
Martin Preuss
e0a6ddd89f avr: removed unneeded def. 2024-09-04 23:18:01 +02:00
Martin Preuss
c02f371350 avr: started creating subdirs for every node. 2024-09-04 23:17:46 +02:00
Martin Preuss
c2a6d33ff2 avr: reduced code (bootloader now 569 words). 2024-09-01 22:29:08 +02:00
Martin Preuss
8aeb488e2e avr: share code (saves 8 bytes) 2024-09-01 20:19:32 +02:00
Martin Preuss
45cae14f6a avr: reuse code (saves a few more bytes). 2024-09-01 20:12:02 +02:00
Martin Preuss
892d9f5c5a avr: fixed a possible problem (generated machine code is shorter). 2024-09-01 20:11:18 +02:00
Martin Preuss
65e60c2dec Replace multiple wait macros with calls (saves 10 bytes). 2024-09-01 20:06:05 +02:00
Martin Preuss
6aac577365 aqhome: fixed compiler warning. 2024-08-25 14:28:43 +02:00
Martin Preuss
85a93065a4 aqhome: added virtual function "flush" 2024-08-25 14:28:22 +02:00
Martin Preuss
319cdbce18 added future message defs 2024-08-25 14:27:13 +02:00
Martin Preuss
c800e99dc8 avr: added comments 2024-08-25 14:26:36 +02:00
Martin Preuss
2da873bbb9 avr: fixed comments. 2024-08-25 14:18:56 +02:00
Martin Preuss
6abac9b5a1 incremented version. 2024-05-19 01:37:12 +02:00
Martin Preuss
ed1f96fdbc aqhome-react: adapted to changes in output name. 2024-05-19 01:36:48 +02:00
Martin Preuss
37a290ce1f aqhome-react: added missing include. 2024-05-19 01:36:30 +02:00
Martin Preuss
a3bf403ac0 aqhome-react: peridocally write vars. 2024-05-19 01:36:09 +02:00
Martin Preuss
dde9106a5d aqhome-react: add "varsFile" (will write variables later). 2024-05-18 13:11:29 +02:00
Martin Preuss
334d0e8096 aqhome: remove flag AQH_PATH_FLAGS_ROOT 2024-05-18 13:10:41 +02:00
Martin Preuss
abada7b89b aqhome: minor format changes. 2024-05-17 20:35:13 +02:00
Martin Preuss
69893640a3 aqhome: added function AQH_Vars_ReplaceVars() 2024-05-17 20:34:31 +02:00
Martin Preuss
9f7f5ab0db aqhome: fixed copyright headers. 2024-05-17 19:25:48 +02:00
Martin Preuss
6828438780 aqhome: add writer for AQH_VARS (including a test). 2024-05-17 19:16:16 +02:00
Martin Preuss
05fb3d3b0a aqhome: added AQH_Vars_Dump(). 2024-05-17 17:39:00 +02:00
Martin Preuss
66c583f866 aqhome: use our own implementation of GWEN_Text_GetWordToBuffer().
use the same flags like the original function (maybe replace the
original function in gwen later).
2024-05-17 17:16:20 +02:00
Martin Preuss
2363535fe0 aqhome: add AQH_Vars_DataTypeToString() 2024-05-17 17:15:12 +02:00
Martin Preuss
9d0037b83d vars: minor beautification. 2024-05-17 01:52:49 +02:00
Martin Preuss
f8f4380038 vars: added code to read AQH_VARS from GWEN_DB strings. 2024-05-17 00:03:51 +02:00
Martin Preuss
ead34f0309 aqhome-react: set module name and description for timeraction. 2024-05-15 22:50:55 +02:00
Martin Preuss
1d51ef0259 aqhome-react: added statistics modules (average, min, max) 2024-05-15 22:50:20 +02:00
Martin Preuss
b888524cc3 aqhome-react: added functions to inc/dec an int value. 2024-05-15 00:17:55 +02:00
Martin Preuss
5ec9827f92 aqhome-react: allow for int values.
those will be used e.g. to count number of open windows etc.
2024-05-15 00:10:15 +02:00
Martin Preuss
7ce34b0500 aqhome-react, aqhome: added units/functions for handling local variables. 2024-05-12 17:31:31 +02:00
Martin Preuss
516ac4e34e vars: added more functions. 2024-05-11 01:07:10 +02:00
Martin Preuss
36e9909060 vars: added more module tests. 2024-05-09 23:08:44 +02:00
Martin Preuss
403392a72e Add module test for AQH_Vars 2024-05-09 14:57:12 +02:00
Martin Preuss
2c584bbff9 vars: use const in most api functions
The idea of not using const was to reduce copy operations.
However, it is not very intuitive to know when and which arguments are const
so to simplify working with this new module and make it as close as possible
to GWEN_DB we use const now as in GWEN_DB.
At least AQH_Vars_SetStringData() still doesn't use const so if the need
arises to avoid copying we can.
2024-05-09 14:56:46 +02:00
Martin Preuss
b6e4a5265a vars: also check idx. 2024-05-09 00:52:26 +02:00
Martin Preuss
b473d62cdc aqhome: more work on path and vars modules. 2024-05-09 00:49:57 +02:00
Martin Preuss
3e5bff90d1 aqhome: make delimiter a function argument instead of hardcoding "/". 2024-05-08 00:41:14 +02:00
Martin Preuss
d5b1dbd5ee aqhome: minor modifications. 2024-05-08 00:20:42 +02:00
Martin Preuss
29eb910881 increased minimum gwen version required. 2024-05-07 23:57:26 +02:00
Martin Preuss
bcc7629b1e aqhome: added PATH module, started VARS module.
those might later get incorporated into libgwenhywfar.
2024-05-07 23:57:07 +02:00
Martin Preuss
f400104bbc aqhome-react: added unit for timer programs. 2024-05-07 23:56:02 +02:00
Martin Preuss
a3d0fad984 aqhome-react: generalize command handling. 2024-04-27 13:24:00 +02:00
Martin Preuss
075fbc1cb5 Added another test for program rules. 2024-04-27 10:55:34 +02:00
Martin Preuss
c9d82cc88e aqhome-react: added program rules with test code. 2024-04-26 01:29:27 +02:00
Martin Preuss
2342dfbe4a incremented version. 2024-04-21 00:34:09 +02:00
Martin Preuss
dbc8fdf35d aqhome-react: decreased verbosity. 2024-04-21 00:34:00 +02:00
Martin Preuss
89dd230b8f aqhome-react: fixed a typo. 2024-04-21 00:33:34 +02:00
Martin Preuss
243754c15d aqhome-react: minor changes to example network file. 2024-04-21 00:33:22 +02:00
Martin Preuss
d17274c6d6 aqhome-react: decreased verbosity. 2024-04-20 19:44:45 +02:00
Martin Preuss
9b2b9dccea aqhome-react: improve log messages. read params of suntimes unit on first process call. 2024-04-20 19:44:31 +02:00
Martin Preuss
83106327fa aqhome-react: increase interval between network file checks. 2024-04-20 19:43:50 +02:00
Martin Preuss
87114cecea aqhome-react: more work on modules and networks.
- tested AND network and new suntime units.
- add unit XML property "invert" (inverts output for logical units)
2024-04-20 17:28:20 +02:00
Martin Preuss
f3c68a8bba aqhome-react: fixed typo. 2024-04-20 02:03:49 +02:00
Martin Preuss
f083fb1c00 aqhome-react: handle MULTI inputs, set input port flags. 2024-04-20 02:03:32 +02:00
Martin Preuss
88d049d68a aqhome-react: added code to determine sunset/sunrise times. 2024-04-20 02:00:06 +02:00
Martin Preuss
7ee7edffec aqhome-react: rename "name" to "typeName" in AQHREACT_Unit 2024-04-18 23:28:27 +02:00
Martin Preuss
e486a7e69d aqhome-react: finish new network reading code, improved debugging helper code. 2024-04-18 22:40:13 +02:00
Martin Preuss
2443fbca9f aqhome-react: added example network. 2024-04-18 22:39:25 +02:00
Martin Preuss
716c1c58df aqhome-react: consistently named function. 2024-04-17 23:13:15 +02:00
Martin Preuss
7ea260031e fixed compiler warnings. 2024-04-17 23:11:52 +02:00
Martin Preuss
02c256ffa2 aqhome-react: replaced example networks. 2024-04-17 23:11:15 +02:00
Martin Preuss
02f02b1ad1 aqhome-react: removed uneeded files. 2024-04-17 22:29:28 +02:00
Martin Preuss
f4902d5717 fixed a compiler warning. 2024-04-17 22:27:02 +02:00
Martin Preuss
8bb60fdba7 build: more gcc warnings. 2024-04-17 22:26:47 +02:00
Martin Preuss
1050ee1c75 aqhome-react: major rebuild of unit handling.
now nested networks are allowed to allow for complex networks.
2024-04-17 22:26:17 +02:00
Martin Preuss
ec816bddcf aqhome-react: added more logical units (or, and, xor). started "module" unit.
module units now are units created from previous networks of units
thus introducing nested units.
2024-04-14 23:42:10 +02:00
Martin Preuss
9468911451 aqhome-react: make more functions virtual. 2024-04-14 23:39:45 +02:00
Martin Preuss
61ce363a8a aqhome-react: sort list of network files before reading. 2024-04-13 00:55:00 +02:00
Martin Preuss
563e5f0eff aqhome-react: add "sourceNet" and "targetNet" to links. 2024-04-13 00:51:32 +02:00
Martin Preuss
ec5aeb9a05 aqhome-react: expand properties also when reading units and links. 2024-04-13 00:41:15 +02:00
Martin Preuss
9a4eb3c608 decrease StartLimitIntervalSec to 1m. 2024-04-13 00:04:03 +02:00
Martin Preuss
dd133c7368 added systemd file for aqhome-react. 2024-04-13 00:03:37 +02:00
Martin Preuss
a7267c061a aqhome-react: extended reading of network files.
network files can now extend a template network making it much easier
to create new networks based on existing one.
2024-04-13 00:03:08 +02:00
Martin Preuss
a94ebeca29 fixed a bug (was not using pathName). 2024-04-12 22:53:46 +02:00
Martin Preuss
1b1d2f6c9e incremented version. 2024-04-12 22:42:00 +02:00
Martin Preuss
83cdc8abe3 aqhome-react: switch locations of template files and system network files.
- template files are stored in $PATH/share/aqhome/react/networks
- system network files are in $PATH/etc/aqhome/react/networks
2024-04-12 22:41:36 +02:00
Martin Preuss
2653b16939 add AQH_FindPathOfDataFile(). 2024-04-12 22:33:56 +02:00
Martin Preuss
ee19014644 aqhome-mqttlog: remove unused var. 2024-04-12 22:33:41 +02:00
Martin Preuss
a479538743 aqhome-react: enable network loading.
This application has now basic functionality.
2024-04-12 21:29:40 +02:00
Martin Preuss
2ac4887f01 aqhome-mqttlog: removed another unneeded file. 2024-04-03 22:47:54 +02:00
Martin Preuss
8a7a4c7a64 aqhome-mqttlog: remove unneeded files. 2024-04-03 22:46:58 +02:00
Martin Preuss
c62165cd92 aqhome-react: Unified function names. 2024-04-03 21:58:34 +02:00
Martin Preuss
5305aa7fe3 Move device files into subfolders (first: devices/mqtt). 2024-04-03 21:57:42 +02:00
Martin Preuss
e29a32f24b Fixed a typo. 2024-03-25 23:18:29 +01:00
Martin Preuss
50bdefcb4a More work on aqhome-react service. 2024-03-25 23:18:18 +01:00
Martin Preuss
02d12b4209 units now use aqh as argument. 2024-03-24 18:51:56 +01:00
Martin Preuss
6f9e20095a aqhome-react: periodically check for network file changes and reload if necessary. 2024-03-24 14:21:20 +01:00
Martin Preuss
e0476924c1 aqhome-react: read network files. 2024-03-24 00:46:13 +01:00
Martin Preuss
2787bb9b79 add missing file to 0BUILD. 2024-03-22 00:08:51 +01:00
Martin Preuss
d92ae455db fixed definition for tasmota devices. 2024-03-22 00:08:36 +01:00
Martin Preuss
f9a5119d1f devices: recognize tasmota plugs out-of-the-box. 2024-03-21 23:41:50 +01:00
Martin Preuss
e162d9564b mqttlog: improved log message. 2024-03-21 23:41:17 +01:00
Martin Preuss
6889d5d851 mqttlog: enable setting data. 2024-03-21 23:41:05 +01:00
Martin Preuss
87d22b1e16 apps: set app log level by command line parameter. 2024-03-21 23:40:44 +01:00
Martin Preuss
30522df662 fixed a typo. 2024-03-19 23:48:24 +01:00
Martin Preuss
ed4fc1852a added function AqHomeReact_AddUnit(). 2024-03-19 23:48:05 +01:00
Martin Preuss
d6494d07d5 fixed a typo. 2024-03-19 23:47:29 +01:00
Martin Preuss
2c8e57ecff Replaced u_hold with more generic u_stabilize. 2024-03-11 21:32:22 +01:00
Martin Preuss
d3a6256c8c More work on aqhome-react.
- added some units
- added some types
2024-03-10 20:15:21 +01:00
Martin Preuss
656c9cf66f No parameters needed for basic units. 2024-03-09 01:35:09 +01:00
Martin Preuss
db19019202 Fixed a bug. 2024-03-09 01:25:11 +01:00
Martin Preuss
5712b041fe Increased timeout for valgrind session of aqhome-data. 2024-03-09 01:24:54 +01:00
Martin Preuss
5925bc58b1 Fixed valgrind script for aqhome-mqtt. 2024-03-09 01:24:34 +01:00
Martin Preuss
2e29790be7 Set scripts to use installed versions of aqhome-tool. 2024-03-09 01:24:11 +01:00
Martin Preuss
0545a802de Added aqhome-mqtt.devices to .gitignore. 2024-03-09 01:23:28 +01:00
Martin Preuss
a3efa18a61 More work on aqhome-react. 2024-03-09 01:23:06 +01:00
Martin Preuss
605d78a2b7 aqhome-react: More work on units. 2024-02-26 21:22:42 +01:00
Martin Preuss
e7bfa36cab add first scripts to get water levels. 2024-02-25 01:22:29 +01:00
Martin Preuss
dce1b8698a aqhome-tool: add command "addjsondata". 2024-02-25 01:21:47 +01:00
Martin Preuss
e4c753cefd aqhome-tool: Fixed a typo (was not setting correct client). 2024-02-25 01:21:24 +01:00
Martin Preuss
9e49e60aa8 aqhome-react: Started working on module which reacts to value changes. 2024-02-21 22:29:02 +01:00
Martin Preuss
4c44890d3c Improved mqttlog daemaon: persistent registered devices. 2024-02-17 17:33:09 +01:00
Martin Preuss
ef22bd65ea mqtt: Allow for empty/missing messages. 2024-02-17 01:10:26 +01:00
Martin Preuss
1ba263fb13 aqhome-mqttlog: Implemented SETDATA. 2024-02-17 01:09:46 +01:00
Martin Preuss
0cf3976fc7 re-implemented SetData command.
Allows for string values to be sent.
2024-02-14 23:07:20 +01:00
Martin Preuss
eeffe225ec Improved mqtt device detection and handling. Add command to announce new values. 2024-02-13 23:49:56 +01:00
Martin Preuss
de1a975586 aqhome-mqttlog: increased verbosity. 2024-02-11 22:22:42 +01:00
Martin Preuss
13d161d119 aqhome-data: fixed a typo. 2024-02-11 22:22:09 +01:00
Martin Preuss
793d12693b aqhome: make datafile a virtual class. Add datafile_direct.
this is to allow for cached data file handling later.
2024-02-11 22:21:49 +01:00
Martin Preuss
5888bc9068 Storage: make storage class virtual with default implementations. 2024-02-11 20:07:30 +01:00
Martin Preuss
b6fe1775bd Improved systemd scripts. 2023-12-18 00:16:19 +01:00
Martin Preuss
90d2467fde Fixed a bug (was not creating new devices for devices similar to existing ones). 2023-12-17 20:54:52 +01:00
Martin Preuss
3c91a83177 Changed debug GCC flags. 2023-10-21 02:19:50 +02:00
Martin Preuss
c7ee9dc18c Fixed bug. 2023-10-21 02:19:30 +02:00
Martin Preuss
62abfd56e9 Added functionality to print a value difference.
Also call GWEN_MsgEndpoint_IoLoop() only after checking all messages in
the endpoint's list.
2023-10-21 02:19:16 +02:00
Martin Preuss
b818065b9b Added function AQH_ValuesDataIpcMsg_GetDatapoints(). 2023-10-21 02:17:48 +02:00
Martin Preuss
c9b88f2cea Add "data" argument to event handlers. 2023-10-18 15:28:32 +02:00
Martin Preuss
f0cfbfccc4 Condensed functions for GetDataPoints to only use one.
Keep HandleGetLastData for now (for older clients).
2023-10-18 15:27:53 +02:00
Martin Preuss
a3f866f69c Renamed files timer* to eventtimer* 2023-10-13 00:48:47 +02:00
Martin Preuss
16e08d623d Started adding an event layer. 2023-10-12 00:35:08 +02:00
Martin Preuss
077b367299 Decreased verbosity, fixed broker connection setup. 2023-10-07 23:06:31 +02:00
Martin Preuss
d1f7a6b730 added munin files. 2023-10-06 18:07:27 +02:00
Martin Preuss
f26c78a5e9 Fixed systemd files (corrected tool paths). 2023-10-06 18:07:08 +02:00
Martin Preuss
0fc18c1da1 add missing files to DIST list. 2023-10-06 18:06:25 +02:00
Martin Preuss
ddded51f9e removed unavailable includes. 2023-10-06 18:06:09 +02:00
Martin Preuss
7b89df6153 sort files in 0BUILD files. 2023-10-06 18:05:46 +02:00
Martin Preuss
161dee1667 remove unneeded includes. 2023-10-06 18:05:27 +02:00
Martin Preuss
e98afa80d9 fixed another memory leak: handle received result responses
just remove them from the queue.
2023-10-06 18:05:14 +02:00
Martin Preuss
38ae2d3d1d correct installation folder for system tools. 2023-10-06 18:04:35 +02:00
Martin Preuss
79c71563d5 removed dependency aqdatabase (not used). 2023-10-06 18:03:47 +02:00
Martin Preuss
fa12be880a Create bigger gource videos. 2023-10-06 14:02:28 +02:00
Martin Preuss
dcdb512995 Finished systemd service files. 2023-10-06 14:02:08 +02:00
Martin Preuss
7fb4e8b577 added systemd service files. added conf example file. 2023-10-06 12:37:19 +02:00
Martin Preuss
7f1c66cbe6 Fixed default value for timeout in aqhome-tool watch 2023-10-06 00:19:57 +02:00
Martin Preuss
c90e8c899a add valgrind caller script for aqhome-mqttlog 2023-10-06 00:17:32 +02:00
Martin Preuss
ab6480ebca Fixed more meory leaks. 2023-10-06 00:17:15 +02:00
Martin Preuss
8613fbdad7 Added handling of timeout cmd arg for valgrind test. fixed memory leaks. 2023-10-05 23:56:53 +02:00
Martin Preuss
b66f3d2ef4 aqhome-tool: added command to watch values changed on the server. 2023-10-04 23:33:40 +02:00
Martin Preuss
f1753eeea7 mqtt module now works. 2023-10-04 18:22:53 +02:00
Martin Preuss
bfed937950 More work on mqtt tool. 2023-10-04 16:02:02 +02:00
Martin Preuss
4730943931 add systemd file for aqhome-data. 2023-10-03 17:37:39 +02:00
Martin Preuss
afc0994c38 Add example comfig file. 2023-10-03 17:37:24 +02:00
Martin Preuss
831c94f898 Read config file for fallback when no command line arguments are given. 2023-10-03 16:51:15 +02:00
Martin Preuss
0740378ad8 Partially reverted one of the last commits to correct created value name. 2023-10-03 13:47:19 +02:00
Martin Preuss
f56b25d06f renamed deviceNameForDriver of AQH_VALUE and nameForDriver of AQH_DEVICE. 2023-10-03 13:42:21 +02:00
Martin Preuss
eadfead77c Removed function AqHomeData_GetOrCreateValueForDriver(). 2023-10-03 13:32:51 +02:00
Martin Preuss
17d4ce5125 Change AQH_Value field NameForDriver to Name and make it only contain the value name.
Previously this field contained the device name, too. This would make it
necessary for drivers to remove the device part of the name when SetValue
is called. Instead the device name is now always provided by the driver in
the appropriate field DeviceNameForDriver.
2023-10-03 13:31:05 +02:00
Martin Preuss
253b3862da add IPC command to modify device info on the server. 2023-10-02 23:22:59 +02:00
Martin Preuss
043541f936 Set timestampCreation on created devices, show that in aqhome-tool. 2023-10-01 23:58:19 +02:00
Martin Preuss
45da38b64a added devices, added command getdevices. 2023-10-01 23:44:26 +02:00
Martin Preuss
c57472d86e removed unneeded IPC message types. 2023-10-01 21:53:55 +02:00
Martin Preuss
1e27223dfa Simplified IPC code to use less different IPC messages. Share more code. More qork on MQTT code. 2023-10-01 21:31:02 +02:00
Martin Preuss
0f896c1729 removed aqhomed. 2023-10-01 21:29:22 +02:00
Martin Preuss
66073737a4 Started reworking mqtt logger tool. 2023-09-20 17:51:24 +02:00
Martin Preuss
bf89562d51 Removed unneeded code. 2023-09-20 17:51:02 +02:00
Martin Preuss
2adefc4b79 Decreased verbosity. 2023-09-20 17:50:20 +02:00
Martin Preuss
f03c078606 decreased verbosity in script. 2023-09-17 19:24:24 +02:00
Martin Preuss
d6ae79cf81 Decreased verbosity. 2023-09-17 19:24:06 +02:00
Martin Preuss
0814cf4af1 added caller script for aqhome-nodes. 2023-09-16 15:49:14 +02:00
Martin Preuss
74789b802f added entries to .gitignore. 2023-09-16 15:48:57 +02:00
Martin Preuss
5f6581d126 Fixed default message size. 2023-09-16 15:48:03 +02:00
Martin Preuss
8c60c3c8e4 cleanup connections every 10s. 2023-09-16 15:47:39 +02:00
Martin Preuss
cf005fa60c increased verbosity. 2023-09-16 15:47:15 +02:00
Martin Preuss
9b7d043682 Started working on aqhome-nodes which will replace aqhomed. 2023-09-13 23:31:02 +02:00
Martin Preuss
161b979e84 added specific IPC messages and use them instead of more generic messages. 2023-09-13 12:07:11 +02:00
Martin Preuss
11798a39d6 Revert "Use INHERIT mechanism for AQH_Tag16IpcMsg."
This reverts commit 07abc76a7a.
2023-09-12 21:33:00 +02:00
Martin Preuss
07abc76a7a Use INHERIT mechanism for AQH_Tag16IpcMsg. 2023-09-12 21:32:51 +02:00
Martin Preuss
e1639a9d13 Implemented setdata in server and aqhome-tool. 2023-09-12 00:04:37 +02:00
Martin Preuss
71f5ce8c7e Implemented GETLASTDATA in server and aqhome-tool. 2023-09-11 22:55:38 +02:00
Martin Preuss
518a3a53f9 Heavy work on IPC.
We will now have a broker (aqhome-data) which stores data and distributes
value change messages among connected clients.
aqhomed will connect to that broker and send its values there.
aqhome-mqtt will also connect to the broker and send its values there.
Other clients can later connect to check for changes and react according
to rules.
2023-09-10 23:13:03 +02:00
Martin Preuss
2b733a52ca Introduces tag16 ipc messages. 2023-09-10 00:22:31 +02:00
Martin Preuss
3bfb39966f aqhome-data: adding datapoints basically works now. 2023-08-17 00:24:38 +02:00
Martin Preuss
f9ae85b9ad more work on IPC data protocol. 2023-08-14 21:38:21 +02:00
Martin Preuss
5fdb33c192 started working on aqhome-data.
this will be the data daemon storing datapoints, accessable via IPC.
2023-08-14 02:00:37 +02:00
Martin Preuss
51a13f286f started work on IPC protocol for data service. 2023-08-13 17:56:31 +02:00
Martin Preuss
290967a7c5 added generic IPC result message. 2023-08-13 17:56:00 +02:00
Martin Preuss
64938b9cb0 Prepared introduction of multiple ipc protocols. 2023-08-13 14:09:15 +02:00
Martin Preuss
590eccf8d9 ADded entries to README. 2023-08-13 13:49:02 +02:00
Martin Preuss
a4c0f2e6fd aqhome-storage now checks and parses mqtt messages and stores values in datafiles. 2023-08-12 16:55:06 +02:00
Martin Preuss
bcd3e3325c added gource script. 2023-08-12 10:54:09 +02:00
Martin Preuss
c1353c056a added list of received and unknown topics. 2023-08-12 10:53:56 +02:00
Martin Preuss
9602471a9b adapted valgrind calling script to analyze aqhome-storage. 2023-08-12 02:07:49 +02:00
Martin Preuss
f5878f43ff Added datafile and handling of MQTT publish message. 2023-08-12 02:06:54 +02:00
Martin Preuss
edcac1f2b9 Fixed required version of aqdatabase. 2023-08-12 02:05:48 +02:00
Martin Preuss
e25a391fde Fixed an important bug (lead to segfaults). 2023-08-11 03:21:31 +02:00
Martin Preuss
978d3f6f7a editing of values now also works. 2023-08-11 03:21:06 +02:00
Martin Preuss
c5171714b2 added url handler for mqtt topics. 2023-08-11 01:24:31 +02:00
Martin Preuss
96c2b9a649 prepared for u_mqtttopic module. 2023-08-10 22:26:30 +02:00
Martin Preuss
487e506a01 added urlhandler for devices. 2023-08-10 19:28:10 +02:00
Martin Preuss
ec745f5cc9 Improved code sharing.
we now have a base url handler which handles listing, adding and editing
any objects including permission management.
2023-08-10 18:03:24 +02:00
Martin Preuss
17889fd30b Simplified code. 2023-08-10 13:28:37 +02:00
Martin Preuss
23c9e286b1 Fixed invalid memory access. 2023-08-10 13:28:24 +02:00
Martin Preuss
a2c79aa6db Fixed memory leaks. 2023-08-10 13:27:51 +02:00
Martin Preuss
f716ebd338 fixed two bugs. 2023-08-10 01:53:10 +02:00
Martin Preuss
9b0122e34c addd urlhandler for static content, more reusing of code. 2023-08-10 01:45:12 +02:00
Martin Preuss
b9a54b8ffb added #define DISABLE_DEBUGLOG to source files in http and service folders
will alter be enabled to disable verbose debug logging.
2023-08-09 17:45:59 +02:00
Martin Preuss
b5916acf79 fixed memory leaks, added cleanup code, added valgrind scripts to test binaries 2023-08-09 17:24:44 +02:00
Martin Preuss
4701a71986 Started experimenting with styles. 2023-08-09 01:57:31 +02:00
Martin Preuss
fc2c18b489 Allow for prefill of form elements, improved I18N. 2023-08-09 00:55:07 +02:00
Martin Preuss
0cc498d830 added TODO remarks. 2023-08-09 00:54:32 +02:00
Martin Preuss
28bc8efac8 Minor format changes. 2023-08-09 00:54:12 +02:00
Martin Preuss
aafecfa704 aqhome: more work on http server. 2023-08-08 23:49:28 +02:00
Martin Preuss
3378908c93 vg_run: rewritten to check aqhomed. 2023-08-07 14:09:51 +02:00
Martin Preuss
e4135a7bbd test: start aqhome locally with only minimal service. 2023-08-07 14:09:27 +02:00
Martin Preuss
1efcd09f0c aqhome: more work on http service. 2023-08-07 14:08:52 +02:00
Martin Preuss
6269431467 aqhome-storage: moved http-specific code to new class. 2023-08-07 14:08:12 +02:00
Martin Preuss
792f167a71 aqhome-storage: started adding class AqHomeHttpService
This will be the class handling HTTP requests for AqHome.
2023-08-07 14:07:30 +02:00
Martin Preuss
897fdffcf9 aqhome: cleanup AQH_HttpService_ParsePostBody(). 2023-07-24 21:59:42 +02:00
Martin Preuss
53e1fbae56 aqhome: removed unneeded classes. 2023-07-24 21:53:31 +02:00
Martin Preuss
16ce958964 aqhome: added service definitions, started implementing HTTP service. 2023-07-24 21:49:17 +02:00
Martin Preuss
db5d6cb980 started working on storage service. 2023-07-19 18:17:10 +02:00
Martin Preuss
06b5ab26c8 libtest.sh: make aqhome loglevel configurable by the caller. 2023-07-19 01:26:51 +02:00
Martin Preuss
02797ff092 aqhome: started working on database code. 2023-07-19 01:26:14 +02:00
Martin Preuss
3eb0a9afa9 aqhome: more work on http endpoint. 2023-07-19 01:25:43 +02:00
Martin Preuss
5852ad9a1e add valgrind log for aqhome-mqttlog. 2023-07-18 10:54:06 +02:00
Martin Preuss
8ee0602f7a modified in-tree start script for aqhome-mqttlog. 2023-07-18 10:53:22 +02:00
Martin Preuss
f8c325e747 modify valgrind script for aqhome-mqttlog. 2023-07-18 10:52:47 +02:00
Martin Preuss
893ae6867b aqhome: started http endpoint. 2023-07-18 10:52:09 +02:00
Martin Preuss
3efb83ecfd aqhome-mqttlog: read all messages available on each loop. 2023-07-18 10:51:16 +02:00
Martin Preuss
f30c4895fa aqhome: removed unused and unneeded code. 2023-07-14 00:02:21 +02:00
Martin Preuss
0fd58567fe adapted to latest changes in gwen (msgio API v2 becomes v1). 2023-07-12 19:30:53 +02:00
Martin Preuss
7a4edb6854 added example systemd scripts. 2023-07-12 16:46:51 +02:00
Martin Preuss
c7f232dd98 decreased verbosity, send MQTT ping every 2 minutes to avoid disconnect. 2023-07-12 16:45:20 +02:00
Martin Preuss
4489314b47 aqhome: removed invalid includes. 2023-07-12 13:34:32 +02:00
Martin Preuss
08c3875a26 aqhome: completed adapting to msgio2 interface. 2023-07-12 13:33:04 +02:00
Martin Preuss
39987b31c7 aqhome: re-implemented aqhomed.
- added IPC endpoint2
2023-07-12 01:45:24 +02:00
Martin Preuss
43b23b2636 msg: added endpoint for tty. 2023-07-10 21:38:22 +02:00
Martin Preuss
2d393630d8 aqhome: added AQH_MqttClientEndpoint2_GetNextPacketId(). added test for subscriptions. 2023-07-09 21:19:36 +02:00
Martin Preuss
5105c0c7f7 aqhome: added functions AQH_ConnAckMqttMsg_GetResultFlags() and AQH_ConnAckMqttMsg_GetResultCode() 2023-07-09 20:48:15 +02:00
Martin Preuss
6f5a26b0cf aqhome: Started rewriting endpoints for version 2 of the msgio interface.
This interface is much simpler.
First rewritten endpoint is that for MQTT.
2023-07-09 20:47:47 +02:00
Martin Preuss
d766a3635a mqtt: started working on 2nd generation msgio implementation. 2023-07-08 01:58:43 +02:00
Martin Preuss
b4175f4a89 added tool aqhome-mqttlog. 2023-05-24 23:08:28 +02:00
Martin Preuss
f0917064af aqhome: fixed mqtt message handling
PUBLISH: the message itself is NOT preceeded by size
2023-05-24 23:07:45 +02:00
Martin Preuss
1751170940 aqhome/mqtt: added messages regarding subscription. 2023-05-14 22:24:55 +02:00
Martin Preuss
3e85dc9bd5 avr: removed unused code. 2023-05-14 22:24:05 +02:00
Martin Preuss
54301fed44 avr/reed: moved notification source to REED_Run.
This make the node send the new status almost immediately after change.
2023-05-13 01:13:28 +02:00
Martin Preuss
efcab6f38d aqhome: only say "open" instead of "fully open". 2023-05-13 00:38:51 +02:00
Martin Preuss
9f32f206b3 aqhome: improved "write" module.
- write window status as string translated from value
- reduced code duplication
2023-05-13 00:15:07 +02:00
Martin Preuss
6a22fb9956 aqhome: write values also to file "value".
- if valueType is known write value into the given file
- always write raw value into file "value"
2023-05-13 00:00:17 +02:00
Martin Preuss
a29a314976 minor docu fix. 2023-05-12 23:48:30 +02:00
Martin Preuss
226922d3e4 avr/reed: implemented tilt detection mode
- implemented tilt detection mode
- added docu
- change the way a VALUE2 message is printed when value type is "door"
2023-05-12 23:19:01 +02:00
Martin Preuss
84403d07f6 avr: added initial module to handle reed contacts.
Detects and reports opening and closing of a window/door.
Nexts step is to allow for external configuration (e.g. standard mode
with one reed contact versus multi-contact mode to detect tilting of a
window/door).
2023-05-12 21:41:39 +02:00
606 changed files with 59745 additions and 5205 deletions

14
.gitignore vendored
View File

@@ -4,3 +4,17 @@ build/
aqhome.db
aqhome.log
aqhomed.vg
aqhome-mqttlog.vg
aqhome-storage.vg
aqhome-storage.pid
aqhome-data.vg
aqhome-data.pid
aqhome-nodes.db
aqhome-nodes.log
aqhome-nodes.pid
aqhome-nodes.vg
aqhome-tool.vg
aqhome-mqtt.pid
aqhome-mqtt.devices
core.*

78
0BUILD
View File

@@ -2,7 +2,7 @@
<gwbuild>
<project name="aqhome" version="0.0.1" so_current="0" so_age="0" so_revision="0" write_config_h="TRUE">
<project name="aqhome" version="0.0.4" so_current="0" so_age="0" so_revision="4" write_config_h="TRUE">
<setVar name="package">$(project_name)</setVar>
<setVar name="version">
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
@@ -44,23 +44,81 @@
<setVar name="includedir">$(option_prefix)/include</setVar>
<setVar name="datarootdir">$(option_prefix)/share</setVar>
<setVar name="datadir">$(option_prefix)/share</setVar>
<setVar name="rtdatadir">$(option_prefix)/var/lib</setVar>
<setVar name="localedir">$(option_prefix)/share/locale</setVar>
<setVar name="pkglibdir">$(libdir)/$(package)</setVar>
<setVar name="pkgincludedir">$(includedir)/aqhome/$(package)</setVar>
<setVar name="pkgdatadir">$(datadir)/$(package)</setVar>
<option id="enable_testcode" type="string">
<default>TRUE</default>
<choices>TRUE FALSE</choices>
</option>
<ifVarMatches name="option_enable_testcode" value="TRUE" >
<define name="AQHOME_ENABLE_TESTCODE" value="1" />
</ifVarMatches>
<option id="local_install" type="string">
<default>FALSE</default>
<choices>TRUE FALSE</choices>
</option>
<ifVarMatches name="option_local_install" value="TRUE" >
<define name="ENABLE_LOCAL_INSTALL" value="1" />
</ifVarMatches>
<option id="debug" type="string">
<default>TRUE</default>
<choices>TRUE FALSE</choices>
</option>
<ifVarMatches name="option_debug" value="TRUE" >
<setVar name="CFLAGS">-ggdb -Wall -O0</setVar>
<setVar name="CXXFLAGS">-ggdb -Wall -O0</setVar>
<setVar name="CFLAGS">
-ggdb -Wall -Og -W
-Wbad-function-cast -Wmissing-parameter-type
-Wnested-externs -Wstringop-overflow=2
-Wdeclaration-after-statement -Wpointer-sign
-Wunused-parameter
</setVar>
<setVar name="CXXFLAGS">-ggdb -Wall -Og</setVar>
</ifVarMatches>
<!-- paths -->
<ifVarMatches name="GWBUILD_SYSTEM" value="windows" > <!-- long version of IF statement with THEN and ELSE -->
<then>
<define name="OS_WIN32" value="1" />
<setVar name="aqhome_sys_is_windows">1</setVar>
<setVar name="aqhome_cfg_searchdir">etc</setVar>
<setVar name="aqhome_locale_searchdir">share/locale</setVar>
<setVar name="aqhome_data_searchdir">share"</setVar>
<setVar name="aqhome_rtdata_searchdir">var/lib</setVar>
</then>
<else>
<define name="OS_POSIX" value="1" />
<setVar name="aqhome_sys_is_windows">0</setVar>
<ifVarMatches name="option_local_install" value="TRUE" >
<then>
<setVar name="aqhome_cfg_searchdir">etc</setVar>
<setVar name="aqhome_locale_searchdir">share/locale</setVar>
<setVar name="aqhome_data_searchdir">share</setVar>
<setVar name="aqhome_rtdata_searchdir">var/lib</setVar>
</then>
<else>
<setVar name="aqhome_cfg_searchdir">$(sysconfdir)</setVar>
<setVar name="aqhome_locale_searchdir">$(datadir)/locale</setVar>
<setVar name="aqhome_data_searchdir">$(datadir)</setVar>
<setVar name="aqhome_rtdata_searchdir">$(rtdatadir)</setVar>
</else>
</ifVarMatches>
</else>
</ifVarMatches>
<define name="AQHOME_SYS_IS_WINDOWS" value="$(aqhome_sys_is_windows)" />
<!-- use "-Owith_avr=TRUE" or "FALSE" to enable or disable assembling AVR code -->
<option id="with_avr" type="string">
<default>TRUE</default>
@@ -88,9 +146,13 @@
<dependencies>
<dep id="gwenhywfar" name="gwenhywfar" minversion="5.8.2.1" required="TRUE" >
<dep id="gwenhywfar" name="gwenhywfar" minversion="5.11.2" required="TRUE" >
<variables>plugindir gwengui-fox16</variables>
</dep>
<!-- <dep id="aqdatabase" name="aqdatabase" minversion="1.99.1" required="TRUE" >
<variables>aqdatabase_typemakerdir</variables>
</dep>
-->
</dependencies>
@@ -110,6 +172,9 @@
<subdirs>
aqhome
apps
devices
etc
scripts
</subdirs>
<ifVarMatches name="option_with_avr" value="TRUE" >
@@ -131,7 +196,10 @@
<output>
</output>
<!-- see http://eleccelerator.com/fusecalc/fusecalc.php?chip=attiny84 -->
<!-- disable divide by 8: -U lfuse:w:0xEA:m default (divide by 8): -U lfuse:w:0x6A:m -->
<!-- changes to hfuse: enable EESAVE -->
<!-- changes to efuse: enable SELFPRGEN -->
<cmd tool="$(avrdude)" checkDates="FALSE" >
-p t84 -c stk500 -P /dev/ttyACM0 -B16 -U hfuse:w:0xD7:m -U efuse:w:0xFE:m -U flash:w:avr/att84_temp1.hex
</cmd>

View File

@@ -3,8 +3,11 @@
<gwbuild>
<subdirs>
aqhomed
aqhome-tool
aqhome-mqttlog
aqhome-data
aqhome-nodes
aqhome-react
</subdirs>
</gwbuild>

1
apps/aqhome-data/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
test/

96
apps/aqhome-data/0BUILD Normal file
View File

@@ -0,0 +1,96 @@
<?xml?>
<gwbuild>
<target type="Program" name="aqhome-data" install="$(sbindir)" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="true" >
aqhome_data.h
aqhome_data_p.h
fini.h
init.h
loop.h
c_connect.h
c_updatedata.h
c_getvalues.h
c_getdevices.h
c_getdatapoints.h
c_getlastdatapoint.h
c_setdata.h
c_addvalue.h
c_annvalue.h
c_moddevice.h
</headers>
<sources>
$(local/typefiles)
aqhome_data.c
fini.c
init.c
loop.c
c_connect.c
c_updatedata.c
c_getvalues.c
c_getdevices.c
c_getdatapoints.c
c_getlastdatapoint.c
c_setdata.c
c_addvalue.c
c_annvalue.c
c_moddevice.c
main.c
</sources>
<useTargets>
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,152 @@
/****************************************************************************
* 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 "./aqhome_data_p.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
AQHOME_DATA *AqHomeData_new()
{
AQHOME_DATA *aqh;
GWEN_NEW_OBJECT(AQHOME_DATA, aqh);
aqh->storageMutex=GWEN_Mutex_new();
aqh->requestTree=GWEN_MsgRequest_new();
return aqh;
}
void AqHomeData_free(AQHOME_DATA *aqh)
{
if (aqh) {
GWEN_Mutex_free(aqh->storageMutex);
GWEN_MsgRequest_free(aqh->requestTree);
GWEN_MsgEndpoint_free(aqh->ipcdEndpoint);
GWEN_DB_Group_free(aqh->dbArgs);
AQH_Storage_free(aqh->storage);
free(aqh->pidFile);
GWEN_FREE_OBJECT(aqh);
}
}
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh)
{
return aqh?(aqh->ipcdEndpoint):NULL;
}
GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh)
{
return aqh?(aqh->dbArgs):NULL;
}
AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh)
{
return aqh?(aqh->storage):NULL;
}
GWEN_MSG_REQUEST *AqHomeData_GetRequestTree(const AQHOME_DATA *aqh)
{
return aqh?aqh->requestTree:NULL;
}
void AqHomeData_AddRequestToTree(AQHOME_DATA *aqh, GWEN_MSG_REQUEST *rq)
{
if (aqh && rq)
GWEN_MsgRequest_Tree2_AddChild(aqh->requestTree, rq);
}
const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh)
{
return aqh?aqh->pidFile:NULL;
}
int AqHomeData_GetTimeout(const AQHOME_DATA *aqh)
{
return aqh?aqh->timeout:0;
}
int AqHomeData_LockStorage(AQHOME_DATA *aqh)
{
int rv;
rv=GWEN_Mutex_Lock(aqh->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex");
return rv;
}
return rv;
}
int AqHomeData_UnlockStorage(AQHOME_DATA *aqh)
{
int rv;
rv=GWEN_Mutex_Unlock(aqh->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex");
return rv;
}
return rv;
}
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcEndpointByServiceName(const AQHOME_DATA *aqh, const char *serviceName)
{
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
const char *s;
s=AQH_IpcEndpoint_GetServiceName(ep);
if (s && *s && strcasecmp(s, serviceName)==0)
return ep;
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
return NULL;
}

View File

@@ -0,0 +1,47 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_H
#define AQHOME_DATA_H
#include "aqhome/data/storage.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/request.h>
typedef struct AQHOME_DATA AQHOME_DATA;
AQHOME_DATA *AqHomeData_new();
void AqHomeData_free(AQHOME_DATA *aqh);
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh);
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcEndpointByServiceName(const AQHOME_DATA *aqh, const char *serviceName);
GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh);
AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh);
const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh);
int AqHomeData_GetTimeout(const AQHOME_DATA *aqh);
int AqHomeData_LockStorage(AQHOME_DATA *aqh);
int AqHomeData_UnlockStorage(AQHOME_DATA *aqh);
GWEN_MSG_REQUEST *AqHomeData_GetRequestTree(const AQHOME_DATA *aqh);
void AqHomeData_AddRequestToTree(AQHOME_DATA *aqh, GWEN_MSG_REQUEST *rq);
#endif

View File

@@ -0,0 +1,44 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_P_H
#define AQHOME_DATA_P_H
#include "./aqhome_data.h"
#include <gwenhywfar/mutex.h>
#define AQHOME_DATA_DEFAULT_PIDFILE "/var/run/aqhome-data.pid"
#define AQHOME_DATA_DEFAULT_DATADIR "/var/lib/aqhome-data/data"
#define AQHOME_DATA_DEFAULT_IPC_PORT 45456
#define AQHOME_DATA_STATEFILENAME "statefile"
struct AQHOME_DATA {
GWEN_MSG_ENDPOINT *ipcdEndpoint;
GWEN_DB_NODE *dbArgs;
AQH_STORAGE *storage;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
GWEN_MUTEX *storageMutex;
GWEN_MSG_REQUEST *requestTree;
};
#endif

View File

@@ -0,0 +1,72 @@
/****************************************************************************
* 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 "./c_addvalue.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleAddValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
AQH_VALUE *recvdValue;
AQH_ValuesDataIpcMsg_Parse(msg, 0);
recvdValue=AQH_ValuesDataIpcMsg_ReadFirstValue(msg);
if (recvdValue) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value==NULL)
resultCode=AQH_MSG_IPC_ERROR_PERMS;
AQH_Value_free(recvdValue);
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_ADDVALUE_H
#define AQHOME_DATA_C_ADDVALUE_H
#include "./aqhome_data.h"
void AqHomeData_HandleAddValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,65 @@
/****************************************************************************
* 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 "./c_annvalue.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleAnnounceValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
AQH_VALUE *recvdValue;
AQH_ValuesDataIpcMsg_Parse(msg, 0);
recvdValue=AQH_ValuesDataIpcMsg_ReadFirstValue(msg);
if (recvdValue) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Could not create announced value");
}
AQH_Value_free(recvdValue);
}
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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_DATA_C_ANNVALUE_H
#define AQHOME_DATA_C_ANNVALUE_H
#include "./aqhome_data.h"
void AqHomeData_HandleAnnounceValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,93 @@
/****************************************************************************
* 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 "./c_connect.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/msg_data_connect.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleConnect(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
char *clientId=NULL;
char *userId=NULL;
char *passw=NULL;
uint32_t flags;
AQH_ConnectDataIpcMsg_Parse(msg, 0);
clientId=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_CLIENTID, NULL);
userId=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_USERID, NULL);
flags=AQH_Tag16IpcMsg_GetTagDataAsUint32(msg, AQH_MSGDATA_CONNECT_TAGS_FLAGS, 0);
passw=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_PASSWORD, NULL);
if (clientId)
AQH_IpcEndpoint_SetServiceName(ep, clientId);
if (userId)
AQH_IpcEndpoint_SetUserName(ep, userId);
if (flags & AQH_MSGDATA_CONNECT_FLAGS_WANTUPDATES)
GWEN_MsgEndpoint_AddFlags(ep, AQH_IPCENDPOINT_FLAGS_WANTUPDATES);
/* TODO: add user management, for now we allow all */
AQH_IpcEndpoint_SetPermissions(ep,
AQH_IPCENDPOINT_PERMS_LISTVALUES |
AQH_IPCENDPOINT_PERMS_READVALUE |
AQH_IPCENDPOINT_PERMS_ADDVALUE |
AQH_IPCENDPOINT_PERMS_LISTDATA |
AQH_IPCENDPOINT_PERMS_READDATA |
AQH_IPCENDPOINT_PERMS_ADDDATA |
AQH_IPCENDPOINT_PERMS_LISTDEVICES |
AQH_IPCENDPOINT_PERMS_READDEVICE |
AQH_IPCENDPOINT_PERMS_ADDDEVICE |
AQH_IPCENDPOINT_PERMS_MODDEVICE);
free(passw);
free(userId);
free(clientId);
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -6,18 +6,20 @@
* should have received along with this file.
****************************************************************************/
#ifndef AQH_ENDPOINT_WRITE_H
#define AQH_ENDPOINT_WRITE_H
#ifndef AQHOME_DATA_C_CONNECT_H
#define AQHOME_DATA_C_CONNECT_H
#include "aqhome/msg/endpoint_node.h"
#include "./aqhome_data.h"
AQHOME_API GWEN_MSG_ENDPOINT *AQH_WriteEndpoint_new(const char *folder, int groupId);
void AqHomeData_HandleConnect(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,211 @@
/****************************************************************************
* 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 "./c_getdatapoints.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/data/msg_data_getdata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _getAndSendDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep,
const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsNoNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId);
static int _getAndSendDataPointsWithNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static void _sendDataPointsResponse(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId);
static void _getAndSendLastDatapoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_READDATA) {
AQH_VALUE *value;
char *valueName;
uint64_t tsBegin;
uint64_t tsEnd;
uint64_t numRequested;
AQH_GetDataDataIpcMsg_Parse(recvdMsg, 0);
valueName=AQH_Tag16IpcMsg_GetTagDataAsNewString(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
tsBegin=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
tsEnd=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_END, 0);
numRequested=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
value=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName);
if (value) {
resultCode=_getAndSendDataPoints(aqh, ep, value, tsBegin, tsEnd, numRequested, GWEN_IpcMsg_GetMsgId(recvdMsg));
if (resultCode==AQH_MSG_IPC_SUCCESS)
return;
}
else {
DBG_INFO(NULL, "Value \"%s\" does not exist", valueName);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
free(valueName);
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
int _getAndSendDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
if (num==0)
return _getAndSendDataPointsNoNum(aqh, ep, value, tsBegin, tsEnd, refMsgId);
else if (num==1) {
_getAndSendLastDatapoint(aqh, ep, value, refMsgId);
return AQH_MSG_IPC_SUCCESS;
}
else
return _getAndSendDataPointsWithNum(aqh, ep, value, num, refMsgId);
}
int _getAndSendDataPointsNoNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetDataPoints(aqh->storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
if (tablePtr) {
_sendDataPointsResponse(aqh, ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSG_IPC_ERROR_NODATA;
}
}
int _getAndSendDataPointsWithNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetLastNDataPoints(aqh->storage, valueId, num);
if (tablePtr) {
_sendDataPointsResponse(aqh, ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSG_IPC_ERROR_NODATA;
}
}
void _sendDataPointsResponse(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId)
{
int numTableEntries;
int numDataPoints;
GWEN_MSG *outMsg;
numTableEntries=(int)(tablePtr[0]);
numDataPoints=numTableEntries/2;
outMsg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
value, &(tablePtr[1]), numDataPoints);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
void _getAndSendLastDatapoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint32_t refMsgId)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
int rv;
uint64_t timestamp=0;
double data=0.0;
rv=AQH_Storage_GetLastDataPoint(aqh->storage, AQH_Value_GetId(value), &timestamp, &data);
if (rv<0) {
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSG_IPC_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSG_IPC_ERROR_NODATA; break;
default: resultCode=AQH_MSG_IPC_ERROR_GENERIC; break;
}
}
else {
outMsg=AQH_MultiDataDataIpcMsg_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
value, timestamp, data);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
return;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETDATAPOINTS_H
#define AQHOME_DATA_C_GETDATAPOINTS_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,106 @@
/****************************************************************************
* 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 "./c_getdevices.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_devices.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_DEVICESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendDeviceList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetDevices(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
const AQH_DEVICE_LIST *origDeviceList;
DBG_INFO(NULL, "HandleGetDevices");
origDeviceList=AQH_Storage_GetDeviceList(aqh->storage);
if (origDeviceList) {
DBG_INFO(NULL, "Have a list of %d devices", AQH_Device_List_GetCount(origDeviceList));
if (AQH_Device_List_GetCount(origDeviceList)<AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending all entries in one message");
_sendDeviceList(aqh, ep, origDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
else {
AQH_DEVICE_LIST *tmpDeviceList;
const AQH_DEVICE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpDeviceList=AQH_Device_List_new();
v=AQH_Device_List_First(origDeviceList);
while(v) {
const AQH_DEVICE *next;
AQH_DEVICE *copyOfDevice;
next=AQH_Device_List_Next(v);
copyOfDevice=AQH_Device_dup(v);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(aqh, ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
AQH_Device_List_Clear(tmpDeviceList);
}
v=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(aqh, ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg)); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
}
else {
/* empty list */
_sendDeviceList(aqh, ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
}
void _sendDeviceList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
GWEN_MSG *msg;
msg=AQH_DevicesDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
flags, vl);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETDEVICES_H
#define AQHOME_DATA_C_GETDEVICES_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetDevices(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,107 @@
/****************************************************************************
* 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 "./c_getlastdatapoint.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_getdata.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_READDATA) {
char *valueName;
AQH_GetDataDataIpcMsg_Parse(recvdMsg, 0);
valueName=AQH_Tag16IpcMsg_GetTagDataAsNewString(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
if (valueName && *valueName) {
const AQH_VALUE *storedValue;
storedValue=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName);
if (storedValue) {
uint64_t timestamp=0;
double data=0.0;
int rv;
rv=AQH_Storage_GetLastDataPoint(aqh->storage, AQH_Value_GetId(storedValue), &timestamp, &data);
if (rv<0) {
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSG_IPC_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSG_IPC_ERROR_NODATA; break;
default: resultCode=AQH_MSG_IPC_ERROR_GENERIC; break;
}
}
else {
outMsg=AQH_MultiDataDataIpcMsg_newForOne(AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
storedValue, timestamp, data);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
free(valueName);
return;
}
}
else {
DBG_INFO(NULL, "Value \"%s\" not found", valueName);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
free(valueName);
}
else {
DBG_INFO(NULL, "No name for value");
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETLASTDATAPOINT_H
#define AQHOME_DATA_C_GETLASTDATAPOINT_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,106 @@
/****************************************************************************
* 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 "./c_getvalues.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_VALUESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendValueList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
const AQH_VALUE_LIST *origValueList;
DBG_INFO(NULL, "HandleGetValues");
origValueList=AQH_Storage_GetValueList(aqh->storage);
if (origValueList) {
DBG_INFO(NULL, "Have a list of %d values", AQH_Value_List_GetCount(origValueList));
if (AQH_Value_List_GetCount(origValueList)<AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending all entries in one message");
_sendValueList(aqh, ep, origValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
else {
AQH_VALUE_LIST *tmpValueList;
const AQH_VALUE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(origValueList);
while(v) {
const AQH_VALUE *next;
AQH_VALUE *copyOfValue;
next=AQH_Value_List_Next(v);
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
if (AQH_Value_List_GetCount(tmpValueList)>=AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(aqh, ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
AQH_Value_List_Clear(tmpValueList);
}
v=next;
}
if (AQH_Value_List_GetCount(tmpValueList)) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(aqh, ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg)); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
}
else {
/* empty list */
_sendValueList(aqh, ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
}
void _sendValueList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
GWEN_MSG *msg;
msg=AQH_ValuesDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
flags, vl);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETVALUES_H
#define AQHOME_DATA_C_GETVALUES_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,114 @@
/****************************************************************************
* 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 "./c_getlastdatapoint.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_devices.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleModDevice(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_MODDEVICE) {
AQH_DEVICE *device;
AQH_DevicesDataIpcMsg_Parse(recvdMsg, 0);
device=AQH_DevicesDataIpcMsg_ReadFirstDevice(recvdMsg);
if (device) {
const char *deviceNameForSystem;
deviceNameForSystem=AQH_Device_GetNameForSystem(device);
if (deviceNameForSystem && *deviceNameForSystem) {
AQH_DEVICE *storedDevice;
storedDevice=AQH_Storage_GetDeviceByNameForSystem(aqh->storage, deviceNameForSystem);
if (storedDevice) {
const char *s;
s=AQH_Device_GetNameForGui(device);
if (s && *s)
AQH_Device_SetNameForGui(storedDevice, s);
s=AQH_Device_GetRoomName(device);
if (s && *s)
AQH_Device_SetRoomName(storedDevice, s);
s=AQH_Device_GetLocation(device);
if (s && *s)
AQH_Device_SetLocation(storedDevice, s);
s=AQH_Device_GetDescription(device);
if (s && *s)
AQH_Device_SetDescription(storedDevice, s);
AQH_Storage_AddRuntimeFlags(aqh->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
resultCode=AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "Device \"%s\" not found", deviceNameForSystem);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No name for value");
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No device info in message");
resultCode=AQH_MSG_IPC_ERROR_INVALID;
}
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_MODDEVICE_H
#define AQHOME_DATA_C_MODDEVICE_H
#include "./aqhome_data.h"
void AqHomeData_HandleModDevice(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

@@ -0,0 +1,258 @@
/****************************************************************************
* 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 "./c_setdata.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc/requests.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_set.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define R_SETDATA_REQUEST_EXPIRE_SECS 20
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static GWEN_MSG_REQUEST *_mkRequest_SetData(AQHOME_DATA *aqh,
GWEN_MSG_ENDPOINT *epSrc, uint32_t requestMsgId,
GWEN_MSG_ENDPOINT *epDriver,
const AQH_VALUE *v, const char *data);
static void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason);
static void _rqAbort(GWEN_MSG_REQUEST *rq, int reason);
static GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epDriver, const AQH_VALUE *v, const char *data);
static int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg);
static void _subRqAbort(GWEN_MSG_REQUEST *rq, int reason);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleSetData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, GWEN_MSG *recvdMsg)
{
uint32_t msgId;
AQH_VALUE *recvdValue;
const char *valueName;
char *valueDataFreeable;
AQH_VALUE *systemValue;
msgId=GWEN_IpcMsg_GetMsgId(recvdMsg);
DBG_INFO(NULL, "Received IPC SetDataRequest message (msgId=%d)", msgId);
AQH_SetDataIpcMsg_Parse(recvdMsg, 0);
recvdValue=AQH_SetDataIpcMsg_ReadValue(recvdMsg);
valueName=recvdValue?AQH_Value_GetNameForSystem(recvdValue):NULL;
valueDataFreeable=AQH_SetDataIpcMsg_ReadData(recvdMsg);
systemValue=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName);
if (systemValue) {
if (AQH_Value_GetValueType(systemValue)==AQH_ValueType_Actor) {
const char *driverName;
driverName=AQH_Value_GetDriver(systemValue);
if (driverName && *driverName) {
GWEN_MSG_ENDPOINT *epDriver;
epDriver=AqHomeData_GetIpcEndpointByServiceName(aqh, driverName);
if (epDriver) {
GWEN_MSG_REQUEST *rq;
DBG_DEBUG(NULL, "Creating SETDATA request for driver endpoint (%s)", GWEN_MsgEndpoint_GetName(epDriver));
rq=_mkRequest_SetData(aqh, epSrc, msgId, epDriver, systemValue, valueDataFreeable);
AqHomeData_AddRequestToTree(aqh, rq);
}
else {
DBG_ERROR(NULL, "Driver \"%s\" not available", driverName);
AQH_IpcEndpoint_SendResponseResult(epSrc, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
}
}
else {
DBG_ERROR(NULL, "No driver name");
AQH_IpcEndpoint_SendResponseResult(epSrc, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
}
}
else {
DBG_ERROR(NULL, "Value \"%s\" is not an actor", valueName);
AQH_IpcEndpoint_SendResponseResult(epSrc, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Unknown value \"%s\"", valueName);
AQH_IpcEndpoint_SendResponseResult(epSrc, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_NOTFOUND);
}
AQH_Value_free(recvdValue);
free(valueDataFreeable);
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
GWEN_MSG_REQUEST *_mkRequest_SetData(AQHOME_DATA *aqh,
GWEN_MSG_ENDPOINT *epSrc, uint32_t requestMsgId,
GWEN_MSG_ENDPOINT *epDriver,
const AQH_VALUE *v, const char *data)
{
GWEN_MSG_REQUEST *rq;
GWEN_MSG_REQUEST *subRq;
rq=GWEN_MsgRequest_new();
GWEN_MsgRequest_SetPrivateData(rq, aqh);
GWEN_MsgRequest_SetEndpoint(rq, epSrc);
GWEN_MsgRequest_SetRequestMsgId(rq, requestMsgId);
GWEN_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
GWEN_MsgRequest_SetAbortFn(rq, _rqAbort);
GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
subRq=_mkSubRequest_SetData(aqh, epDriver, v, data);
GWEN_MsgRequest_Tree2_AddChild(rq, subRq);
return rq;
}
void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason)
{
GWEN_MSG_ENDPOINT *ep;
uint32_t refMsgId;
int result;
DBG_DEBUG(NULL, "SubRequest finished (reason: %d)", reason);
refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq);
ep=GWEN_MsgRequest_GetEndpoint(rq);
result=GWEN_MsgRequest_GetResult(subRq);
if (reason==GWEN_MSG_REQUEST_REASON_ABORTED)
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
else
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, result);
GWEN_MsgRequest_SetResult(rq, result);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
}
void _rqAbort(GWEN_MSG_REQUEST *rq, int reason)
{
GWEN_MSG_ENDPOINT *ep;
uint32_t refMsgId;
GWEN_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq);
ep=GWEN_MsgRequest_GetEndpoint(rq);
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
/* ------------------------------------------------------------------------------------------------
* Driver Request SETDATA
*/
GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epDriver, const AQH_VALUE *v, const char *data)
{
GWEN_MSG_REQUEST *rq;
uint16_t msgId;
GWEN_MSG *driverMsg;
rq=GWEN_MsgRequest_new();
GWEN_MsgRequest_SetPrivateData(rq, aqh);
GWEN_MsgRequest_SetEndpoint(rq, epDriver);
GWEN_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
GWEN_MsgRequest_SetAbortFn(rq, _subRqAbort);
msgId=GWEN_MsgEndpoint_GetNextMessageId(epDriver);
GWEN_MsgRequest_SetRequestMsgId(rq, msgId);
GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
driverMsg=AQH_SetDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data);
GWEN_MsgEndpoint_AddSendMessage(epDriver, driverMsg);
return rq;
}
int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg)
{
DBG_DEBUG(NULL, "Checking message from driver");
if (GWEN_IpcMsg_GetCode(msg)==AQH_MSGTYPE_IPC_DATA_RESULT) {
uint32_t result;
GWEN_MSG_REQUEST *rqParent;
result=AQH_ResultIpcMsg_GetResultCode(msg);
DBG_INFO(NULL, "Received result for request: %d", result);
GWEN_MsgRequest_SetResult(rq, result);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, GWEN_MSG_REQUEST_REASON_DONE);
return GWEN_MSG_REQUEST_RESULT_HANDLED;
}
else {
DBG_ERROR(NULL, "Unexpected response message %d", GWEN_IpcMsg_GetCode(msg));
}
return GWEN_MSG_REQUEST_RESULT_NOT_HANDLED;
}
void _subRqAbort(GWEN_MSG_REQUEST *rq, int reason)
{
GWEN_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
GWEN_MsgRequest_SetResult(rq, AQH_MSG_IPC_ERROR_GENERIC);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_SETDATA_H
#define AQHOME_DATA_C_SETDATA_H
#include "./aqhome_data.h"
void AqHomeData_HandleSetData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

@@ -0,0 +1,153 @@
/****************************************************************************
* 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 "./c_updatedata.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define DISABLE_DEBUGLOG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _storeDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues);
static void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
const uint64_t *dataPoints, uint32_t numValues);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleUpdateData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
const GWEN_TAG16 *tag;
AQH_VALUE *recvdValue;
const char *valueName;
const uint64_t *dataPoints=NULL;
unsigned int numberOfPoints=0;
AQH_MultiDataDataIpcMsg_Parse(recvdMsg, 0);
recvdValue=AQH_MultiDataDataIpcMsg_ReadValue(recvdMsg);
valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL;
tag=AQH_Tag16IpcMsg_FindFirstTagByType(recvdMsg, AQH_MSGDATA_MULTIDATA_TAGS_DATA);
dataPoints=tag?((const uint64_t*)GWEN_Tag16_GetTagData(tag)):NULL;
numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t));
if (numberOfPoints>0) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value) {
resultCode=_storeDataPoints(aqh, value, dataPoints, numberOfPoints);
if (resultCode==AQH_MSG_IPC_SUCCESS)
_sendDataChangedMsgToAllClients(aqh, ep, value, dataPoints, numberOfPoints);
}
else {
DBG_INFO(NULL, "No permissions to add datapoint for value \"%s\"", valueName);
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
}
else {
DBG_INFO(NULL, "No datapoints");
resultCode=AQH_MSG_IPC_ERROR_INVALID;
}
AQH_Value_free(recvdValue);
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
int _storeDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues)
{
uint32_t i;
for(i=0; i<numValues; i++) {
uint64_t timestamp;
union {double f; uint64_t i;} u;
int rv;
timestamp=*(dataPoints++);
u.i=*(dataPoints++);
rv=AQH_Storage_AddDatapoint(aqh->storage, AQH_Value_GetId(v), timestamp, u.f);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return AQH_MSG_IPC_ERROR_GENERIC;
}
else {
DBG_DEBUG(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
}
} /* for */
return AQH_MSG_IPC_SUCCESS;
}
void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
const uint64_t *dataPoints, uint32_t numValues)
{
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
if (ep!=epSrc) {
if (GWEN_MsgEndpoint_GetFlags(ep) & AQH_IPCENDPOINT_FLAGS_WANTUPDATES) {
GWEN_MSG *msg;
DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint %s", GWEN_MsgEndpoint_GetName(ep));
msg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
GWEN_MsgEndpoint_GetNextMessageId(ep), 0,
v, dataPoints, numValues);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s doesn't want updates", GWEN_MsgEndpoint_GetName(ep));
}
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Not sending update msg to source of updates");
}
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_C_UPDATEDATA_H
#define AQHOME_DATA_C_UPDATEDATA_H
#include "./aqhome_data.h"
void AqHomeData_HandleUpdateData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

80
apps/aqhome-data/fini.c Normal file
View File

@@ -0,0 +1,80 @@
/****************************************************************************
* 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 "./fini.h"
#include "./aqhome_data_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <unistd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _disconnectTree(GWEN_MSG_ENDPOINT *ep);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_Fini(AQHOME_DATA *aqh)
{
if (aqh) {
if (aqh->ipcdEndpoint) {
_disconnectTree(aqh->ipcdEndpoint);
GWEN_MsgEndpoint_Disconnect(aqh->ipcdEndpoint);
}
GWEN_MsgEndpoint_free(aqh->ipcdEndpoint);
aqh->ipcdEndpoint=NULL;
if (aqh->pidFile)
remove(aqh->pidFile);
}
}
void _disconnectTree(GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG_ENDPOINT *epChild;
epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep);
while(epChild) {
_disconnectTree(epChild);
epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild);
} /* while */
GWEN_MsgEndpoint_Disconnect(ep);
}

23
apps/aqhome-data/fini.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_FINI_H
#define AQHOME_DATA_FINI_H
#include "./aqhome_data.h"
void AqHomeData_Fini(AQHOME_DATA *aqh);
#endif

341
apps/aqhome-data/init.c Normal file
View File

@@ -0,0 +1,341 @@
/****************************************************************************
* 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 "./init.h"
#include "./aqhome_data_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/endpoint_msgio.h>
#include <gwenhywfar/directory.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _setupStorage(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs);
static void _setupIpc(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs);
static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data);
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
static int _createPidFile(const char *pidFilename);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int AqHomeData_Init(AQHOME_DATA *aqh, int argc, char **argv)
{
GWEN_DB_NODE *dbArgs;
int rv;
const char *s;
dbArgs=GWEN_DB_Group_new("args");
rv=_readArgs(argc, argv, dbArgs);
if (rv<0) {
DBG_ERROR(NULL, "Error reading args (%d)", rv);
return rv;
}
AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile");
aqh->dbArgs=dbArgs;
s=GWEN_DB_GetCharValue(dbArgs, "loglevel", 0, NULL);
if (s && *s) {
GWEN_LOGGER_LEVEL ll;
ll=GWEN_Logger_Name2Level(s);
GWEN_Logger_SetLevel(NULL, ll);
}
aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_DATA_DEFAULT_PIDFILE);
if (s && *s) {
free(aqh->pidFile);
aqh->pidFile=strdup(s);
rv=_createPidFile(s);
if (rv<0) {
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
return rv;
}
}
rv=_setupStorage(aqh, dbArgs);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
_setupIpc(aqh, dbArgs);
return 0;
}
int _setupStorage(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs)
{
const char *dataFolder;
GWEN_BUFFER *nameBuf;
AQH_STORAGE *sto;
int rv;
dataFolder=GWEN_DB_GetCharValue(dbArgs, "dataFolder", 0, AQHOME_DATA_DEFAULT_DATADIR);
nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(nameBuf, "%s%s%s", dataFolder, GWEN_DIR_SEPARATOR_S, AQHOME_DATA_STATEFILENAME);
sto=AQH_Storage_new();
AQH_Storage_SetStateFile(sto, GWEN_Buffer_GetStart(nameBuf));
AQH_Storage_SetDataFileFolder(sto, dataFolder);
GWEN_Buffer_free(nameBuf);
rv=AQH_Storage_Init(sto);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQH_Storage_free(sto);
return rv;
}
aqh->storage=sto;
return 0;
}
void _setupIpc(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs)
{
const char *tcpAddress;
int tcpPort;
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL);
if (!(tcpAddress && *tcpAddress))
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, NULL);
tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, -1);
if (tcpPort<0)
tcpPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOME_DATA_DEFAULT_IPC_PORT);
if (tcpAddress && *tcpAddress && tcpPort>0) {
GWEN_MSG_ENDPOINT *ep;
DBG_INFO(NULL, "Starting TCP service on \"%s\":%d", tcpAddress, tcpPort);
ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0);
GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh);
aqh->ipcdEndpoint=ep;
}
}
GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep,
GWEN_SOCKET *sk,
const GWEN_INETADDRESS *addr,
GWEN_UNUSED void *data)
{
/* AQHOME_DATA *aqh;
*
* aqh=(AQHOME_DATA*) data;
*/
DBG_INFO(NULL, "Incoming IPC connection");
return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, 0);
}
int _createPidFile(const char *pidFilename)
{
FILE *f;
int pidfd;
if (remove(pidFilename)==0) {
DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)");
}
#ifdef HAVE_SYS_STAT_H
pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (pidfd < 0) {
DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
f = fdopen(pidfd, "w");
#else /* HAVE_STAT_H */
f=fopen(pidFilename,"w+");
#endif /* HAVE_STAT_H */
/* write pid */
#ifdef HAVE_GETPID
fprintf(f,"%d\n",getpid());
#else
fprintf(f,"-1\n");
#endif
if (fclose(f)) {
DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
return 0;
}
int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
{
int rv;
const GWEN_ARGS args[]= {
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"loglevel", /* name */
0, /* minnum */
1, /* maxnum */
"L", /* short option */
"loglevel", /* long option */
I18S("Specify loglevel"), /* short description */
I18S("Specify loglevel") /* long description */
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"tcpAddress", /* name */
0, /* minnum */
1, /* maxnum */
"t", /* short option */
"tcpaddress", /* long option */
I18S("Specify the TCP address to listen on (disabled if missing)"),
I18S("Specify the TCP address to listen on (disabled if missing)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"tcpPort", /* name */
0, /* minnum */
1, /* maxnum */
"P", /* short option */
"tcpport", /* long option */
I18S("Specify the TCP port to listen on"),
I18S("Specify the TCP port to listen on")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"datafolder", /* name */
0, /* minnum */
1, /* maxnum */
NULL, /* short option */
"datafolder", /* long option */
I18S("Folder where data files are stored"),
I18S("Folder where data files are stored")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"pidfile", /* name */
0, /* minnum */
1, /* maxnum */
"p", /* short option */
"pidfile", /* long option */
I18S("Specify the PID file"),
I18S("Specify the PID file")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"timeout", /* name */
0, /* minnum */
1, /* maxnum */
"T", /* short option */
"timeout", /* long option */
I18S("Specify timeout in second (default: no timeout)"),
I18S("Specify timeout in second (default: no timeout)")
},
{
GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */
GWEN_ArgsType_Int, /* type */
"help", /* name */
0, /* minnum */
0, /* maxnum */
"h", /* short option */
"help",
I18S("Show this help screen."),
I18S("Show this help screen.")
}
};
rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs);
if (rv==GWEN_ARGS_RESULT_ERROR) {
fprintf(stderr, "ERROR: Could not parse arguments main\n");
return GWEN_ERROR_INVALID;
}
else if (rv==GWEN_ARGS_RESULT_HELP) {
GWEN_BUFFER *ubuf;
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
GWEN_Buffer_AppendArgs(ubuf,
I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"),
AQHOME_VERSION_STRING,
argv[0]);
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
fprintf(stderr, "ERROR: Could not create help string\n");
return 1;
}
GWEN_Buffer_AppendString(ubuf, "\n");
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf));
GWEN_Buffer_free(ubuf);
return GWEN_ERROR_CLOSE;
}
return 0;
}

23
apps/aqhome-data/init.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_INIT_H
#define AQHOME_DATA_INIT_H
#include "./aqhome_data.h"
int AqHomeData_Init(AQHOME_DATA *aqh, int argc, char **argv);
#endif

260
apps/aqhome-data/loop.c Normal file
View File

@@ -0,0 +1,260 @@
/****************************************************************************
* 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 "./loop.h"
#include "./c_connect.h"
#include "./c_updatedata.h"
#include "./c_getdatapoints.h"
#include "./c_getlastdatapoint.h"
#include "./c_getvalues.h"
#include "./c_getdevices.h"
#include "./c_setdata.h"
#include "./c_addvalue.h"
#include "./c_annvalue.h"
#include "./c_moddevice.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/data/msg_data_datapoints.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/requests.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/msg_ipc.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define DISABLE_DEBUGLOG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _readAndHandleIpcMessages(AQHOME_DATA *aqh);
static void _handleIpcEndpoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep);
static void _handleIpcMsg(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
static AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epDriver, const char *nameForDriver);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_Loop(AQHOME_DATA *aqh, int timeoutInMsecs)
{
if (aqh) {
GWEN_MsgEndpoint_IoLoop(aqh->ipcdEndpoint, timeoutInMsecs);
_readAndHandleIpcMessages(aqh);
AQH_Requests_CheckTimeouts(aqh->requestTree);
AQH_Requests_Cleanup(aqh->requestTree);
}
}
int AqHomeData_WriteStorageIfChanged(AQHOME_DATA *aqh)
{
if (AQH_Storage_GetRuntimeFlags(aqh->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) {
int rv;
DBG_INFO(NULL, "Storage modified, writing statefile");
rv=AqHomeData_LockStorage(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error locking storage (%d)", rv);
return rv;
}
rv=AQH_Storage_WriteState(aqh->storage);
if (rv<0) {
DBG_INFO(NULL, "Error writing state file (%d)", rv);
AqHomeData_UnlockStorage(aqh);
return rv;
}
rv=AqHomeData_UnlockStorage(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error unlocking storage (%d)", rv);
return rv;
}
}
return 0;
}
AQH_VALUE *AqHomeData_GetOrCreateValueForDriverWithTemplate(AQHOME_DATA *aqh,
GWEN_MSG_ENDPOINT *epDriver,
const AQH_VALUE *valueTemplate)
{
const char *serviceName;
AQH_VALUE *v;
GWEN_BUFFER *buf;
const char *valueName;
const char *deviceName;
serviceName=AQH_IpcEndpoint_GetServiceName(epDriver);
valueName=AQH_Value_GetName(valueTemplate);
deviceName=AQH_Value_GetDeviceName(valueTemplate);
buf=GWEN_Buffer_new(0, 256, 0, 1);
if (deviceName && *deviceName)
GWEN_Buffer_AppendArgs(buf, "%s/%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName, valueName);
else
GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", valueName);
v=AQH_Storage_GetValueByNameForSystem(aqh->storage, GWEN_Buffer_GetStart(buf));
if (v==NULL) {
if (AQH_IpcEndpoint_GetPermissions(epDriver) & AQH_IPCENDPOINT_PERMS_ADDVALUE) {
AQH_DEVICE *device;
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf));
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(aqh, epDriver, deviceName):NULL;
v=AQH_Value_new();
AQH_Value_SetDriver(v, serviceName);
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
AQH_Value_SetNameForSystem(v, GWEN_Buffer_GetStart(buf));
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
AQH_Value_SetTimestampCreation(v, (uint64_t) time(NULL));
if (device) {
AQH_Value_SetDeviceNameForSystem(v, AQH_Device_GetNameForSystem(device));
AQH_Value_SetDeviceName(v, AQH_Device_GetName(device));
}
AQH_Storage_AddValue(aqh->storage, v);
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
return NULL;
}
}
GWEN_Buffer_free(buf);
return v;
}
AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epDriver, const char *deviceName)
{
const char *serviceName;
AQH_DEVICE *device;
GWEN_BUFFER *buf;
serviceName=AQH_IpcEndpoint_GetServiceName(epDriver);
buf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName);
device=AQH_Storage_GetDeviceByNameForSystem(aqh->storage, GWEN_Buffer_GetStart(buf));
if (device==NULL) {
if (AQH_IpcEndpoint_GetPermissions(epDriver) & AQH_IPCENDPOINT_PERMS_ADDDEVICE) {
DBG_INFO(AQH_LOGDOMAIN, "Creating device \"%s\"", GWEN_Buffer_GetStart(buf));
device=AQH_Device_new();
AQH_Device_SetDriver(device, serviceName);
AQH_Device_SetName(device, deviceName);
AQH_Device_SetNameForSystem(device, GWEN_Buffer_GetStart(buf));
AQH_Device_SetTimestampCreation(device, (uint64_t) time(NULL));
AQH_Storage_AddDevice(aqh->storage, device);
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create device \"%s\"", GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
return NULL;
}
}
GWEN_Buffer_free(buf);
return device;
}
void _readAndHandleIpcMessages(AQHOME_DATA *aqh)
{
if (aqh->ipcdEndpoint) {
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
_handleIpcEndpoint(aqh, ep);
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
}
}
void _handleIpcEndpoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG *msg;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) {
DBG_INFO(NULL, "Got IPS message %d (msgId=%d, refMsgId=%d) [%s]",
GWEN_IpcMsg_GetCode(msg),
GWEN_IpcMsg_GetMsgId(msg),
GWEN_IpcMsg_GetRefMsgId(msg),
GWEN_MsgEndpoint_GetName(ep));
if (AQH_Requests_HandleIpcMsg(aqh->requestTree, ep, msg)!=GWEN_MSG_REQUEST_RESULT_HANDLED)
_handleIpcMsg(aqh, ep, msg);
GWEN_Msg_free(msg);
}
}
void _handleIpcMsg(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
uint16_t code;
uint8_t protoId;
/* exec IPC message */
code=GWEN_IpcMsg_GetCode(msg);
protoId=GWEN_IpcMsg_GetProtoId(msg);
if (protoId==AQH_IPC_PROTOCOL_DATA_ID) {
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC packet %d (%x)", (int) code, code);
switch(code) {
case AQH_MSGTYPE_IPC_DATA_CONNECT_REQ: AqHomeData_HandleConnect(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: AqHomeData_HandleUpdateData(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeData_HandleGetValues(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: AqHomeData_HandleGetDataPoints(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_GETLASTDATA_REQ: AqHomeData_HandleGetLastDataPoint(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeData_HandleSetData(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_ADDVALUE: AqHomeData_HandleAddValue(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE: AqHomeData_HandleAnnounceValue(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ: AqHomeData_HandleGetDevices(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ: AqHomeData_HandleModDevice(aqh, ep, msg); break;
default: break;
}
}
else {
DBG_ERROR(NULL, "Invalid IPC protocol %d (%02x)", protoId, protoId);
}
}

30
apps/aqhome-data/loop.h Normal file
View File

@@ -0,0 +1,30 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_DATA_LOOP_H
#define AQHOME_DATA_LOOP_H
#include "./aqhome_data.h"
void AqHomeData_Loop(AQHOME_DATA *aqh, int timeoutInMsecs);
int AqHomeData_WriteStorageIfChanged(AQHOME_DATA *aqh);
AQH_VALUE *AqHomeData_GetOrCreateValueForDriverWithTemplate(AQHOME_DATA *aqh,
GWEN_MSG_ENDPOINT *epDriver,
const AQH_VALUE *valueTemplate);
#endif

253
apps/aqhome-data/main.c Normal file
View File

@@ -0,0 +1,253 @@
/****************************************************************************
* 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 <aqhome/api.h>
#include <aqhome/aqhome.h>
#include "./aqhome_data.h"
#include "./init.h"
#include "./fini.h"
#include "./loop.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/cgui.h>
#include <gwenhywfar/debug.h>
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#define DISABLE_DEBUGLOG
//#define WRITE_INTERVAL_IN_SECS (5*60)
#define WRITE_INTERVAL_IN_SECS 60
#define PING_INTERVAL 120
#define CONNCLEAN_INTERVAL_IN_SECS 10
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
#ifdef HAVE_SIGNAL_H
static int _setSignalHandlers(void);
static int _setupSigAction(struct sigaction *sa, int sig);
static void _signalHandler(int s);
#endif
static void _runService(AQHOME_DATA *aqh);
static void _writeCurrentState(AQHOME_DATA *aqh);
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
#ifdef HAVE_SIGNAL_H
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT;
#endif
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
int rv;
AQHOME_DATA *aqh;
GWEN_GUI *gui;
rv=GWEN_Init();
if (rv) {
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
return 2;
}
GWEN_Logger_Open(0, "aqhome-data", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
rv=_setSignalHandlers();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
rv=AQH_Init();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
gui=GWEN_Gui_CGui_new();
GWEN_Gui_SetGui(gui);
aqh=AqHomeData_new();
rv=AqHomeData_Init(aqh, argc, argv);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh);
AqHomeData_Fini(aqh);
AqHomeData_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQHOME_DATA *aqh)
{
time_t timeStart;
time_t timeLastWrite;
time_t timeLastConnectionCleanup;
int timeout;
timeout=AqHomeData_GetTimeout(aqh);
timeStart=time(NULL);
timeLastWrite=time(NULL);
timeLastConnectionCleanup=time(NULL);
while(!stopService) {
time_t now;
DBG_DEBUG(NULL, "Next loop");
AqHomeData_Loop(aqh, 2000);
now=time(NULL);
if (((int)difftime(now, timeLastConnectionCleanup))>CONNCLEAN_INTERVAL_IN_SECS) {
DBG_DEBUG(NULL, "Cleanup connections");
GWEN_MsgEndpoint_RemoveUnconnectedAndEmptyChildren(AqHomeData_GetIpcdEndpoint(aqh));
timeLastConnectionCleanup=now;
}
if (((int)difftime(now, timeLastWrite))>WRITE_INTERVAL_IN_SECS) {
DBG_DEBUG(NULL, "Write time");
_writeCurrentState(aqh);
timeLastWrite=now;
}
if (timeout && ((int)difftime(now, timeStart))>timeout) {
DBG_INFO(NULL, "Timeout");
_writeCurrentState(aqh);
break;
}
} /* while */
}
void _writeCurrentState(AQHOME_DATA *aqh)
{
int rv;
rv=AqHomeData_WriteStorageIfChanged(aqh);
if (rv<0) {
DBG_ERROR(NULL, "ATTENTION: Could not write storage statefile (%d)", rv);
}
}
int _setSignalHandlers(void)
{
#ifdef HAVE_SIGNAL_H
int rv;
rv=_setupSigAction(&saINT, SIGINT);
if (rv)
return rv;
rv=_setupSigAction(&saTERM, SIGTERM);
if (rv)
return rv;
rv=_setupSigAction(&saHUP, SIGHUP);
if (rv)
return rv;
# ifdef SIGTSTP
rv=_setupSigAction(&saTSTP, SIGTSTP);
if (rv)
return rv;
# endif
# ifdef SIGCONT
rv=_setupSigAction(&saCONT, SIGCONT);
if (rv)
return rv;
# endif
#endif
return 0;
}
int _setupSigAction(struct sigaction *sa, int sig)
{
sa->sa_handler=_signalHandler;
sigemptyset(&sa->sa_mask);
sa->sa_flags=0;
if (sigaction(sig, sa, 0)) {
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
return GWEN_ERROR_IO;
}
return 0;
}
void _signalHandler(int s)
{
switch(s) {
case SIGINT:
case SIGTERM:
case SIGHUP:
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
stopService=1;
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}

View File

@@ -0,0 +1,89 @@
<?xml?>
<gwbuild>
<target type="Program" name="aqhome-mqttlog" install="$(sbindir)" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="true" >
init.h
fini.h
loop.h
loop_ipc.h
loop_mqtt.h
aqhome_mqtt.h
aqhome_mqtt_p.h
xmlread.h
xmlwrite.h
c_setdata.h
</headers>
<sources>
$(local/typefiles)
aqhome_mqtt.c
init.c
fini.c
loop.c
loop_ipc.c
loop_mqtt.c
main.c
xmlread.c
xmlwrite.c
c_setdata.c
</sources>
<useTargets>
aqhome
aqhmqtt_types
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
types
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,189 @@
/****************************************************************************
* 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 "./aqhome_mqtt_p.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/endpoint_ipcclient.h"
#include "aqhome/mqtt/endpoint_mqttc.h"
#include "aqhome/mqtt/msg_mqtt_publish.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
AQHOME_MQTT *AqHomeMqtt_new(void)
{
AQHOME_MQTT *aqh;
GWEN_NEW_OBJECT(AQHOME_MQTT, aqh);
return aqh;
}
void AqHomeMqtt_free(AQHOME_MQTT *aqh)
{
if (aqh) {
AQHMQTT_Device_List_free(aqh->availableDeviceList);
AQHMQTT_Device_List_free(aqh->registeredDeviceList);
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
GWEN_DB_Group_free(aqh->dbArgs);
free(aqh->pidFile);
GWEN_FREE_OBJECT(aqh);
}
}
GWEN_MSG_ENDPOINT *AqHomeMqtt_GetBrokerEndpoint(const AQHOME_MQTT *aqh)
{
return aqh?(aqh->brokerEndpoint):NULL;
}
GWEN_MSG_ENDPOINT *AqHomeMqtt_GetMqttEndpoint(const AQHOME_MQTT *aqh)
{
return aqh?(aqh->mqttEndpoint):NULL;
}
GWEN_DB_NODE *AqHomeMqtt_GetDbArgs(const AQHOME_MQTT *aqh)
{
return aqh?(aqh->dbArgs):NULL;
}
const char *AqHomeMqtt_GetPidFile(const AQHOME_MQTT *aqh)
{
return aqh?aqh->pidFile:NULL;
}
void AqHomeMqtt_SetPidFile(AQHOME_MQTT *aqh, const char *s)
{
if (aqh) {
free(aqh->pidFile);
aqh->pidFile=s?strdup(s):NULL;
}
}
int AqHomeMqtt_GetTimeout(const AQHOME_MQTT *aqh)
{
return aqh?aqh->timeout:0;
}
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;
}
void AqHomeMqtt_SetAvailableDeviceList(AQHOME_MQTT *aqh, AQHMQTT_DEVICE_LIST *dl)
{
if (aqh) {
AQHMQTT_Device_List_free(aqh->availableDeviceList);
aqh->availableDeviceList=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) {
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

@@ -0,0 +1,51 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_MQTT_H
#define AQHOME_MQTT_H
//#include "./mqttvalue.h"
//#include "./mqtttopic.h"
#include "aqhome-mqttlog/types/device.h"
#include <gwenhywfar/endpoint.h>
typedef struct AQHOME_MQTT AQHOME_MQTT;
AQHOME_MQTT *AqHomeMqtt_new(void);
void AqHomeMqtt_free(AQHOME_MQTT *aqh);
GWEN_MSG_ENDPOINT *AqHomeMqtt_GetBrokerEndpoint(const AQHOME_MQTT *aqh);
GWEN_MSG_ENDPOINT *AqHomeMqtt_GetMqttEndpoint(const AQHOME_MQTT *aqh);
GWEN_DB_NODE *AqHomeMqtt_GetDbArgs(const AQHOME_MQTT *aqh);
const char *AqHomeMqtt_GetPidFile(const AQHOME_MQTT *aqh);
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

@@ -0,0 +1,45 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_MQTT_P_H
#define AQHOME_MQTT_P_H
#include "./aqhome_mqtt.h"
#include <gwenhywfar/mutex.h>
#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"
struct AQHOME_MQTT {
GWEN_MSG_ENDPOINT *rootEndpoint;
GWEN_MSG_ENDPOINT *brokerEndpoint; /* do not free (is part of tree pointed to by rootEndpoint) */
GWEN_MSG_ENDPOINT *mqttEndpoint; /* do not free (is part of tree pointed to by rootEndpoint) */
GWEN_DB_NODE *dbArgs;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
AQHMQTT_DEVICE_LIST *availableDeviceList;
AQHMQTT_DEVICE_LIST *registeredDeviceList;
char *deviceFile;
};
#endif

View File

@@ -0,0 +1,160 @@
/****************************************************************************
* 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 "./c_setdata.h"
#include "aqhome/data/value.h"
#include "aqhome/ipc/data/msg_data_set.h"
#include "aqhome/mqtt/msg_mqtt_publish.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendDataForDevice(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const char *valueName, const char *valueData);
static void _sendValueToMqtt(AQHOME_MQTT *aqh, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData);
static GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeMqttLog_HandleSetData(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
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_GetName(recvdValue):NULL;
deviceName=recvdValue?AQH_Value_GetDeviceName(recvdValue):NULL;
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, "Either value name or device name missing in request");
}
AQH_Value_free(recvdValue);
}
else {
DBG_ERROR(NULL, "Request does not contain a value object");
}
}
void _sendDataForDevice(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const char *valueName, const char *valueData)
{
const char *deviceId;
deviceId=AQHMQTT_Device_GetId(device);
if (deviceId && *deviceId) {
AQHMQTT_TOPIC_LIST *topicList;
topicList=AQHMQTT_Device_GetTopicList(device);
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=AQHMQTT_Topic_List_First(topicList);
while(topic) {
if (AQHMQTT_Topic_GetDirection(topic)==AQHMQTT_TopicDir_Out) {
AQHMQTT_VALUE_LIST *valueList;
AQHMQTT_VALUE *value;
valueList=AQHMQTT_Topic_GetValueList(topic);
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 */
topic=AQHMQTT_Topic_List_Next(topic);
} /* while topic */
}
}
else {
DBG_ERROR(NULL, "Device has no id");
}
}
void _sendValueToMqtt(AQHOME_MQTT *aqh, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData)
{
GWEN_MSG_ENDPOINT *ep;
GWEN_BUFFER *buf;
GWEN_MSG *msgOut;
ep=AqHomeMqtt_GetMqttEndpoint(aqh);
buf=_createBufferForTopic(deviceId, topic);
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);
}
else {
DBG_ERROR(NULL, "Error creating message");
}
GWEN_Buffer_free(buf);
}
GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic)
{
GWEN_BUFFER *buf;
const char *s;
buf=GWEN_Buffer_new(0, 256, 0, 1);
s=AQHMQTT_Topic_GetBeforeId(topic);
if (s && *s)
GWEN_Buffer_AppendString(buf, s);
GWEN_Buffer_AppendString(buf, deviceId);
s=AQHMQTT_Topic_GetAfterId(topic);
if (s && *s)
GWEN_Buffer_AppendString(buf, s);
return buf;
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_MQTTLOG_C_SETDATA_H
#define AQHOME_MQTTLOG_C_SETDATA_H
#include "./aqhome_mqtt.h"
void AqHomeMqttLog_HandleSetData(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

@@ -0,0 +1,85 @@
/****************************************************************************
* 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 "./fini.h"
#include "./aqhome_mqtt_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <unistd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _disconnectTree(GWEN_MSG_ENDPOINT *ep);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeMqtt_Fini(AQHOME_MQTT *aqh)
{
if (aqh) {
if (aqh->rootEndpoint)
_disconnectTree(aqh->rootEndpoint);
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
aqh->rootEndpoint=NULL;
aqh->brokerEndpoint=NULL;
aqh->mqttEndpoint=NULL;
AQHMQTT_Device_List_free(aqh->availableDeviceList);
aqh->availableDeviceList=NULL;
AQHMQTT_Device_List_free(aqh->registeredDeviceList);
aqh->registeredDeviceList=NULL;
if (aqh->pidFile)
remove(aqh->pidFile);
}
}
void _disconnectTree(GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG_ENDPOINT *epChild;
epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep);
while(epChild) {
_disconnectTree(epChild);
epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild);
} /* while */
GWEN_MsgEndpoint_Disconnect(ep);
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMEMQTT_FINI_H
#define AQHOMEMQTT_FINI_H
#include "./aqhome_mqtt.h"
void AqHomeMqtt_Fini(AQHOME_MQTT *aqh);
#endif

486
apps/aqhome-mqttlog/init.c Normal file
View File

@@ -0,0 +1,486 @@
/****************************************************************************
* 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 "./init.h"
#include "./aqhome_mqtt_p.h"
#include "./xmlread.h"
#include "./xmlwrite.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/endpoint_ipcclient.h"
#include "aqhome/mqtt/endpoint_mqttc.h"
#include <gwenhywfar/endpoint_multilayer.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _createPidFile(const char *pidFilename);
static int _setupBroker(AQHOME_MQTT *aqh, GWEN_DB_NODE *dbArgs);
static int _setupMqtt(AQHOME_MQTT *aqh, GWEN_DB_NODE *dbArgs);
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int AqHomeMqtt_Init(AQHOME_MQTT *aqh, int argc, char **argv)
{
int rv;
GWEN_DB_NODE *dbArgs;
const char *s;
dbArgs=GWEN_DB_Group_new("args");
rv=_readArgs(argc, argv, dbArgs);
if (rv<0) {
DBG_ERROR(NULL, "Error reading args (%d)", rv);
return rv;
}
AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile");
aqh->dbArgs=dbArgs;
s=GWEN_DB_GetCharValue(dbArgs, "loglevel", 0, NULL);
if (s && *s) {
GWEN_LOGGER_LEVEL ll;
ll=GWEN_Logger_Name2Level(s);
GWEN_Logger_SetLevel(NULL, ll);
}
aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_MQTT_DEFAULT_PIDFILE);
if (s && *s) {
AqHomeMqtt_SetPidFile(aqh, s);
rv=_createPidFile(s);
if (rv<0) {
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
return rv;
}
}
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);
if (rv<0) {
DBG_ERROR(NULL, "Error setting up connection to broker (%d)", rv);
return rv;
}
rv=_setupBroker(aqh, dbArgs);
if (rv<0) {
DBG_ERROR(NULL, "Error setting up connection to broker (%d)", rv);
return rv;
}
AqHomeMqtt_LoadRuntimeDeviceFiles(aqh);
AqHomeMqtt_ReloadDeviceFiles(aqh);
return 0;
}
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);
}
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;
int pidfd;
if (remove(pidFilename)==0) {
DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)");
}
#ifdef HAVE_SYS_STAT_H
pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (pidfd < 0) {
DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
f = fdopen(pidfd, "w");
#else /* HAVE_STAT_H */
f=fopen(pidFilename,"w+");
#endif /* HAVE_STAT_H */
/* write pid */
#ifdef HAVE_GETPID
fprintf(f,"%d\n",getpid());
#else
fprintf(f,"-1\n");
#endif
if (fclose(f)) {
DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
return 0;
}
int _setupBroker(AQHOME_MQTT *aqh, GWEN_DB_NODE *dbArgs)
{
const char *brokerAddress;
int brokerPort;
const char *brokerClientId;
brokerAddress=GWEN_DB_GetCharValue(dbArgs, "brokerAddress", 0, NULL);
if (!(brokerAddress && *brokerAddress))
brokerAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, "127.0.0.1");
brokerPort=GWEN_DB_GetIntValue(dbArgs, "brokerPort", 0, -1);
if (brokerPort<0)
brokerPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOME_MQTT_DEFAULT_BROKER_PORT);
brokerClientId=GWEN_DB_GetCharValue(dbArgs, "brokerClientId", 0, AQHOME_MQTT_DEFAULT_BROKER_CLIENTID);
if (brokerAddress && *brokerAddress && brokerPort) {
GWEN_MSG_ENDPOINT *ep;
GWEN_MSG_ENDPOINT *ipcBaseEndpoint;
int rv;
ep=AQH_ClientIpcEndpoint_new("brokerIpcClient", 0);
ipcBaseEndpoint=AQH_IpcEndpoint_CreateIpcTcpClient(brokerAddress, brokerPort, "brokerPhysEndpoint", 0);
AQH_IpcEndpoint_SetServiceName(ipcBaseEndpoint, brokerClientId);
GWEN_MsgEndpoint_Tree2_AddChild(ep, ipcBaseEndpoint);
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
aqh->brokerEndpoint=ep;
rv=GWEN_MultilayerEndpoint_StartConnect(ep);
if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) {
DBG_ERROR(NULL, "Error connecting to broker server %s:%d (%d), will retry later", brokerAddress, brokerPort, rv);
return rv;
}
}
return 0;
}
int _setupMqtt(AQHOME_MQTT *aqh, GWEN_DB_NODE *dbArgs)
{
const char *mqttAddress;
int mqttPort;
const char *mqttClientId;
int mqttKeepAlive;
mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL);
if (!(mqttAddress && *mqttAddress))
mqttAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/mqttAddr", 0, "127.0.0.1");
mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, 1883);
if (mqttPort<0)
mqttPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/mqttPort", 0, 1883);
mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, "aqhome-mqttlog");
mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, 600);
if (mqttAddress && *mqttAddress && mqttPort) {
GWEN_MSG_ENDPOINT *epMqtt;
DBG_INFO(AQH_LOGDOMAIN, "Connecting to %s (port %d)", mqttAddress, mqttPort);
epMqtt=AQH_MqttClientEndpoint_new(mqttClientId, mqttAddress, mqttPort, NULL, 0);
if (epMqtt==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Error creating endpoint TCP");
return GWEN_ERROR_IO;
}
AQH_MqttClientEndpoint_SetKeepAliveTime(epMqtt, mqttKeepAlive);
GWEN_MsgEndpoint_AddFlags(epMqtt, AQH_ENDPOINT2_MQTTCLIENT_FLAGS_SUBSCRIBEALL);
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, epMqtt);
aqh->mqttEndpoint=epMqtt;
}
return 0;
}
int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
{
int rv;
const GWEN_ARGS args[]= {
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"loglevel", /* name */
0, /* minnum */
1, /* maxnum */
"L", /* short option */
"loglevel", /* long option */
I18S("Specify loglevel"), /* short description */
I18S("Specify loglevel") /* long description */
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"cfgdir", /* name */
0, /* minnum */
1, /* maxnum */
"D", /* short option */
"cfgdir", /* long option */
I18S("Specify the configuration folder"),
I18S("Specify the configuration folder")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"charset", /* name */
0, /* minnum */
1, /* maxnum */
0, /* short option */
"charset", /* long option */
I18S("Specify the output character set"), /* short description */
I18S("Specify the output character set") /* long description */
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"brokerAddress", /* name */
0, /* minnum */
1, /* maxnum */
"ba", /* short option */
"brokeraddress", /* long option */
I18S("Specify the address of the broker server to connect to (disabled if missing)"),
I18S("Specify the address of the broker server to connect to (disabled if missing)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"brokerPort", /* name */
0, /* minnum */
1, /* maxnum */
"bp", /* short option */
"brokerport", /* long option */
I18S("Specify the port of the broker server (default: 1899)"),
I18S("Specify the port of the broker server (default: 1899)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"brokerClientId", /* name */
0, /* minnum */
1, /* maxnum */
NULL, /* short option */
"brokerclientid", /* long option */
I18S("Specify client id for the broker server (default: \"nodes\")"),
I18S("Specify client id for the broker server (default: \"nodes\")")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"mqttAddress", /* name */
0, /* minnum */
1, /* maxnum */
"ma", /* short option */
"mqttaddress", /* long option */
I18S("Specify the address of the MQTT server to connect to (disabled if missing)"),
I18S("Specify the address of the MQTT server to connect to (disabled if missing)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"mqttPort", /* name */
0, /* minnum */
1, /* maxnum */
"mp", /* short option */
"mqttport", /* long option */
I18S("Specify the port of the MQTT server (default: 1883)"),
I18S("Specify the port of the MQTT server (default: 1883)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"mqttClientId", /* name */
0, /* minnum */
1, /* maxnum */
NULL, /* short option */
"mqttclientid", /* long option */
I18S("Specify client id for the MQTT server (default: \"aqhomed\")"),
I18S("Specify client id for the MQTT server (default: \"aqhomed\")")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"mqttKeepAlive", /* name */
0, /* minnum */
1, /* maxnum */
"mk", /* short option */
"mqttkeepalive", /* long option */
I18S("Specify keepalive time in seconds (defaults: 600)"),
I18S("Specify keepalive time in seconds (defaults: 600)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"pidfile", /* name */
0, /* minnum */
1, /* maxnum */
"p", /* short option */
"pidfile", /* long option */
I18S("Specify the PID file"),
I18S("Specify the PID file")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"timeout", /* name */
0, /* minnum */
1, /* maxnum */
"T", /* short option */
"timeout", /* long option */
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 */
"help", /* name */
0, /* minnum */
0, /* maxnum */
"h", /* short option */
"help",
I18S("Show this help screen."),
I18S("Show this help screen.")
}
};
rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs);
if (rv==GWEN_ARGS_RESULT_ERROR) {
fprintf(stderr, "ERROR: Could not parse arguments main\n");
return GWEN_ERROR_INVALID;
}
else if (rv==GWEN_ARGS_RESULT_HELP) {
GWEN_BUFFER *ubuf;
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
GWEN_Buffer_AppendArgs(ubuf,
I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"),
AQHOME_VERSION_STRING,
argv[0]);
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
fprintf(stderr, "ERROR: Could not create help string\n");
return 1;
}
GWEN_Buffer_AppendString(ubuf, "\n");
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf));
GWEN_Buffer_free(ubuf);
return GWEN_ERROR_CLOSE;
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMEMQTT_INIT_H
#define AQHOMEMQTT_INIT_H
#include "./aqhome_mqtt.h"
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

@@ -0,0 +1,57 @@
/****************************************************************************
* 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 "./loop.h"
#include "./loop_ipc.h"
#include "./loop_mqtt.h"
#include "./c_setdata.h"
#include "./aqhome_mqtt_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/json_read.h>
#include <gwenhywfar/db.h>
//#define FULL_DEBUG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeMqttLog_Loop(AQHOME_MQTT *aqh, int timeoutInMsecs)
{
if (aqh) {
GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs);
AqHomeMqttLog_ReadAndHandleMqttMessages(aqh);
AqHomeMqttLog_ReadAndHandleIpcMessages(aqh);
}
}

View File

@@ -6,17 +6,21 @@
* should have received along with this file.
****************************************************************************/
#ifndef AQH_ENDPOINT_NODE_IPC_TCPD_H
#define AQH_ENDPOINT_NODE_IPC_TCPD_H
#ifndef AQHOMEMQTT_LOOP_H
#define AQHOMEMQTT_LOOP_H
#include <aqhome/api.h>
#include "./aqhome_mqtt.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
AQHOME_API GWEN_MSG_ENDPOINT *AQH_TcpdIpcNodeEndpoint_new(const char *host, int port, const char *name, int groupId);
void AqHomeMqttLog_Loop(AQHOME_MQTT *aqh, int timeoutInMsecs);
#endif

View File

@@ -0,0 +1,87 @@
/****************************************************************************
* 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 "./loop_ipc.h"
#include "./aqhome_mqtt_p.h"
#include "./c_setdata.h"
#include "aqhome/ipc/data/ipc_data.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/json_read.h>
#include <gwenhywfar/db.h>
#define FULL_DEBUG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleIpcMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeMqttLog_ReadAndHandleIpcMessages(AQHOME_MQTT *aqh)
{
GWEN_MSG_ENDPOINT *epTcp;
GWEN_MSG *msg;
epTcp=aqh->brokerEndpoint;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(epTcp)) ) {
_handleIpcMsg(aqh, epTcp, msg);
GWEN_Msg_free(msg);
}
}
void _handleIpcMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
uint16_t code;
uint8_t protoId;
/* exec IPC message */
code=GWEN_IpcMsg_GetCode(msg);
protoId=GWEN_IpcMsg_GetProtoId(msg);
if (protoId==AQH_IPC_PROTOCOL_DATA_ID) {
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC packet %d (%x)", (int) code, code);
switch(code) {
case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeMqttLog_HandleSetData(aqh, ep, msg); break;
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

@@ -0,0 +1,26 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMEMQTT_LOOP_IPC_H
#define AQHOMEMQTT_LOOP_IPC_H
#include "./aqhome_mqtt.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
void AqHomeMqttLog_ReadAndHandleIpcMessages(AQHOME_MQTT *aqh);
#endif

View File

@@ -0,0 +1,501 @@
/****************************************************************************
* 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 "./loop_mqtt.h"
#include "./aqhome_mqtt_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/mqtt/msg_mqtt_publish.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/data/ipc_data.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/json_read.h>
#include <gwenhywfar/db.h>
//#define FULL_DEBUG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleMqttMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
static void _handlePublishMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
static int _handlePublish(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const char *topic, const char *value);
static void _handleNumTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, AQHMQTT_DEVICE *dev, AQHMQTT_TOPIC *t, const char *rcvdValue);
static void _handleJsonTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, AQHMQTT_DEVICE *dev, AQHMQTT_TOPIC *t, const char *rcvdValue);
static void _sendMessage(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue);
static void _announceDeviceToBroker(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device);
static void _sendAnnounceValueMessage(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value);
static AQH_VALUE *_mkMessageValue(const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value);
static int _mqttValueTypeMessageValueType(int t);
static int _registerNewDeviceForTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const char *rcvdTopic, const char *rcvdValue);
static AQHMQTT_TOPIC *_findMaskMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir);
static AQHMQTT_TOPIC *_findTopicMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir);
static GWEN_BUFFER *_extractDeviceId(const AQHMQTT_TOPIC *topic, const char *rcvdTopic);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeMqttLog_ReadAndHandleMqttMessages(AQHOME_MQTT *aqh)
{
GWEN_MSG_ENDPOINT *epTcp;
GWEN_MSG *msg;
epTcp=aqh->mqttEndpoint;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(epTcp)) ) {
#ifdef FULL_DEBUG
DBG_ERROR(NULL, "Received this message:");
GWEN_Text_DumpString((const char*) GWEN_Msg_GetConstBuffer(msg), GWEN_Msg_GetBytesInBuffer(msg), 2);
#endif
_handleMqttMsg(aqh, epTcp, msg);
GWEN_Msg_free(msg);
}
}
int AqHomeMqttLog_SendPing(AQHOME_MQTT *aqh)
{
GWEN_MSG_ENDPOINT *epTcp;
GWEN_MSG *msgOut;
DBG_INFO(AQH_LOGDOMAIN, "Sending PING");
epTcp=aqh->mqttEndpoint;
msgOut=GWEN_MqttMsg_new(AQH_MQTTMSG_MSGTYPE_PINGREQ, 0, NULL);
if (msgOut==NULL) {
DBG_ERROR(NULL, "Error creating message");
return GWEN_ERROR_INTERNAL;
}
GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut);
return 0;
}
void _handleMqttMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
if ((AQH_MqttMsg_GetMsgTypeAndFlags(msg) & 0xf0)==(AQH_MQTTMSG_MSGTYPE_PUBLISH & 0xf0)) {
DBG_INFO(AQH_LOGDOMAIN, "PUBLISH message received");
#ifdef FULL_DEBUG
GWEN_BUFFER *buf;
buf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_PublishMqttMsg_DumpToBuffer(msg, buf, "received");
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
#endif
_handlePublishMsg(aqh, ep, msg);
}
else if ((AQH_MqttMsg_GetMsgTypeAndFlags(msg) & 0xf0)==(AQH_MQTTMSG_MSGTYPE_PINGRESP & 0xf0)) {
DBG_INFO(AQH_LOGDOMAIN, "PING response received");
}
else {
#ifdef FULL_DEBUG
DBG_ERROR(NULL, "Received this message:");
GWEN_Text_DumpString((const char*) GWEN_Msg_GetConstBuffer(msg), GWEN_Msg_GetBytesInBuffer(msg), 2);
#endif
}
}
void _handlePublishMsg(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
char *topic;
char *value;
topic=AQH_PublishMqttMsg_ExtractTopic(msg);
value=AQH_PublishMqttMsg_ExtractValue(msg);
if (topic && value) {
int rv;
rv=_handlePublish(aqh, ep, topic, value);
if (rv!=1) {
DBG_INFO(NULL, "New topic \"%s\", trying to register", topic);
rv=_registerNewDeviceForTopic(aqh, ep, topic, value);
if (rv==1) {
rv=_handlePublish(aqh, ep, topic, value);
if (rv!=1) {
DBG_ERROR(NULL, "Topic \"%s\" still not handled, SNH!", topic);
}
}
}
}
else {
DBG_ERROR(NULL, "Either topic or value missing in PUBLISH msg");
}
free(value);
free(topic);
}
int _handlePublish(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const char *rcvdTopic, const char *rcvdValue)
{
if (rcvdTopic && *rcvdTopic) {
if (aqh->registeredDeviceList) {
AQHMQTT_DEVICE *device;
device=AQHMQTT_Device_List_First(aqh->registeredDeviceList);
while(device) {
AQHMQTT_TOPIC_LIST *topicList;
const char *sDeviceName;
const char *sDeviceId;
sDeviceName=AQHMQTT_Device_GetName(device);
sDeviceId=AQHMQTT_Device_GetId(device);
topicList=AQHMQTT_Device_GetTopicList(device);
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=_findTopicMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
#if 0
if (topic==NULL) {
topic=_findMaskMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
if (topic)
AQHMQTT_Topic_SetTopic(topic, rcvdTopic);
}
#endif
if (topic) {
DBG_INFO(AQH_LOGDOMAIN,
"Handling topic \"%s\" for device type %s (id: %s)",
rcvdTopic,
sDeviceName, sDeviceId?sDeviceId:"<no id>");
if (AQHMQTT_Topic_GetTopicType(topic)==AQHMQTT_TopicType_Json)
_handleJsonTopic(aqh, ep, device, topic, rcvdValue);
else
_handleNumTopic(aqh, ep, device, topic, rcvdValue);
return 1;
}
}
device=AQHMQTT_Device_List_Next(device);
}
}
DBG_INFO(AQH_LOGDOMAIN, "ignoring topic \"%s\"", rcvdTopic);
}
return 0;
}
void _handleNumTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, AQHMQTT_DEVICE *device, AQHMQTT_TOPIC *topic, const char *rcvdValue)
{
AQHMQTT_VALUE_LIST *valueList;
valueList=AQHMQTT_Topic_GetValueList(topic);
if (valueList)
_sendMessage(aqh, device, AQHMQTT_Value_List_First(valueList), rcvdValue);
else {
DBG_INFO(NULL, "No value list in device \"%s\"", AQHMQTT_Device_GetId(device));
}
}
void _handleJsonTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, AQHMQTT_DEVICE *device, AQHMQTT_TOPIC *topic, const char *rcvdValue)
{
GWEN_JSON_ELEM *jeRoot;
jeRoot=GWEN_JsonElement_fromString(rcvdValue);
if (jeRoot==NULL) {
DBG_INFO(NULL, "Could not parse JSON value: %s", rcvdValue);
}
else {
AQHMQTT_VALUE_LIST *valueList;
valueList=AQHMQTT_Topic_GetValueList(topic);
if (valueList) {
AQHMQTT_VALUE *value;
value=AQHMQTT_Value_List_First(valueList);
while(value) {
const char *path;
path=AQHMQTT_Value_GetPath(value);
if (path && *path) {
GWEN_JSON_ELEM *je;
je=GWEN_JsonElement_GetElementByPath(jeRoot, path, 0);
if (je) {
const char *s;
s=GWEN_JsonElement_GetData(je);
if (s && *s)
_sendMessage(aqh, device, value, s);
}
}
value=AQHMQTT_Value_List_Next(value);
} /* while */
}
GWEN_JsonElement_free(jeRoot);
}
}
void _sendMessage(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue)
{
int rv;
union {double f; uint64_t i;} u;
const char *deviceName;
deviceName=AQHMQTT_Device_GetId(device);
rv=GWEN_Text_StringToDouble(rcvdValue, &(u.f));
if (rv<0) {
DBG_ERROR(NULL, "Invalid value received from MQTT server (%s)", rcvdValue?rcvdValue:"<empty>");
}
else {
GWEN_MSG *pubMsg;
uint64_t arrayToSend[2];
AQH_VALUE *msgValue;
arrayToSend[0]=(uint64_t) time(NULL);
arrayToSend[1]=u.i;
msgValue=_mkMessageValue(device, value);
pubMsg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
GWEN_MsgEndpoint_GetNextMessageId(aqh->brokerEndpoint), 0,
msgValue, arrayToSend, 1);
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN, "BROKER UPDATE_DATA %s/%s: %f",
deviceName?deviceName:"<no device name>",
AQH_Value_GetName(msgValue), u.f);
GWEN_MsgEndpoint_AddSendMessage(aqh->brokerEndpoint, pubMsg);
}
AQH_Value_free(msgValue);
}
}
void _announceDeviceToBroker(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device)
{
AQHMQTT_TOPIC_LIST *topicList;
topicList=AQHMQTT_Device_GetTopicList(device);
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=AQHMQTT_Topic_List_First(topicList);
while(topic) {
AQHMQTT_VALUE_LIST *valueList;
valueList=AQHMQTT_Topic_GetValueList(topic);
if (valueList) {
const AQHMQTT_VALUE *value;
value=AQHMQTT_Value_List_First(valueList);
while(value) {
_sendAnnounceValueMessage(aqh, device, value);
value=AQHMQTT_Value_List_Next(value);
}
}
topic=AQHMQTT_Topic_List_Next(topic);
}
}
}
void _sendAnnounceValueMessage(AQHOME_MQTT *aqh, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value)
{
GWEN_MSG *pubMsg;
AQH_VALUE *msgValue;
msgValue=_mkMessageValue(device, value);
pubMsg=AQH_ValuesDataIpcMsg_newForOneValue(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
GWEN_MsgEndpoint_GetNextMessageId(aqh->brokerEndpoint), 0,
0, msgValue);
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN, "BROKER ANNOUNCE_VALUE %s", AQH_Value_GetName(msgValue));
GWEN_MsgEndpoint_AddSendMessage(aqh->brokerEndpoint, pubMsg);
}
AQH_Value_free(msgValue);
}
AQH_VALUE *_mkMessageValue(const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value)
{
AQH_VALUE *msgValue;
msgValue=AQH_Value_new();
AQH_Value_SetDeviceName(msgValue, AQHMQTT_Device_GetId(device));
AQH_Value_SetName(msgValue, AQHMQTT_Value_GetName(value));
AQH_Value_SetValueUnits(msgValue, AQHMQTT_Value_GetValueUnits(value));
AQH_Value_SetValueType(msgValue, _mqttValueTypeMessageValueType(AQHMQTT_Value_GetValueType(value)));
return msgValue;
}
int _mqttValueTypeMessageValueType(int t)
{
switch(t){
case AQHMQTT_ValueType_Sensor: return AQH_ValueType_Sensor;
case AQHMQTT_ValueType_Actor: return AQH_ValueType_Actor;
default: break;
}
DBG_ERROR(AQH_LOGDOMAIN, "Invalid mqtt value type %d", t);
return AQH_ValueType_Sensor;
}
int _registerNewDeviceForTopic(AQHOME_MQTT *aqh, GWEN_MSG_ENDPOINT *ep, const char *rcvdTopic, const char *rcvdValue)
{
if (rcvdTopic && *rcvdTopic) {
if (aqh->availableDeviceList) {
AQHMQTT_DEVICE *device;
device=AQHMQTT_Device_List_First(aqh->availableDeviceList);
while(device) {
AQHMQTT_TOPIC_LIST *topicList;
topicList=AQHMQTT_Device_GetTopicList(device);
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=_findMaskMatchingTopic(topicList, rcvdTopic, AQHMQTT_TopicDir_In);
if (topic) {
GWEN_BUFFER *buf;
buf=_extractDeviceId(topic, rcvdTopic);
if (buf) {
AQHMQTT_DEVICE *newDevice;
newDevice=AQHMQTT_Device_dup(device);
AQHMQTT_Device_SetId(newDevice, GWEN_Buffer_GetStart(buf));
topic=_findMaskMatchingTopic(AQHMQTT_Device_GetTopicList(newDevice), rcvdTopic, AQHMQTT_TopicDir_In);
AQHMQTT_Topic_SetTopic(topic, rcvdTopic);
if (aqh->registeredDeviceList==NULL)
aqh->registeredDeviceList=AQHMQTT_Device_List_new();
DBG_ERROR(NULL, "Registered device \"%s\" (%s)", AQHMQTT_Device_GetId(newDevice), AQHMQTT_Device_GetName(newDevice));
AQHMQTT_Device_List_Add(newDevice, aqh->registeredDeviceList);
_announceDeviceToBroker(aqh, newDevice);
GWEN_Buffer_free(buf);
return 1;
}
}
}
device=AQHMQTT_Device_List_Next(device);
}
}
DBG_INFO(AQH_LOGDOMAIN, "ignoring topic \"%s\"", rcvdTopic);
}
return 0;
}
AQHMQTT_TOPIC *_findMaskMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir)
{
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=AQHMQTT_Topic_List_First(topicList);
while(topic) {
if (AQHMQTT_Topic_GetDirection(topic)==dir) {
const char *sMask;
sMask=AQHMQTT_Topic_GetMask(topic);
if (sMask && *sMask && GWEN_Text_ComparePattern(rcvdTopic, sMask, 0)!=-1) {
/* found a matching topic */
return topic;
}
}
topic=AQHMQTT_Topic_List_Next(topic);
}
}
return NULL;
}
AQHMQTT_TOPIC *_findTopicMatchingTopic(AQHMQTT_TOPIC_LIST *topicList, const char *rcvdTopic, int dir)
{
if (topicList) {
AQHMQTT_TOPIC *topic;
topic=AQHMQTT_Topic_List_First(topicList);
while(topic) {
if (AQHMQTT_Topic_GetDirection(topic)==dir) {
const char *sTopic;
sTopic=AQHMQTT_Topic_GetTopic(topic);
if (sTopic && *sTopic && strcasecmp(rcvdTopic, sTopic)==0) {
return topic;
}
}
topic=AQHMQTT_Topic_List_Next(topic);
}
}
return NULL;
}
GWEN_BUFFER *_extractDeviceId(const AQHMQTT_TOPIC *topic, const char *rcvdTopic)
{
const char *sBefore;
const char *sAfter;
sBefore=AQHMQTT_Topic_GetBeforeId(topic);
sAfter=AQHMQTT_Topic_GetAfterId(topic);
if (sBefore && *sBefore && sAfter && *sAfter) {
GWEN_BUFFER *buf;
int rv;
buf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendString(buf, rcvdTopic);
rv=GWEN_Buffer_KeepTextBetweenStrings(buf, sBefore, sAfter, 1);
if (rv<0) {
DBG_INFO(NULL, "Could not extract id from [%s] (beforeId=%s, afterId=%s)", rcvdTopic, sBefore, sAfter);
GWEN_Buffer_free(buf);
return NULL;
}
return buf;
}
return NULL;
}

View File

@@ -6,19 +6,22 @@
* should have received along with this file.
****************************************************************************/
#ifndef AQH_ENDPOINT_NODE_IPC_H
#define AQH_ENDPOINT_NODE_IPC_H
#ifndef AQHOMEMQTT_LOOP_MQTT_H
#define AQHOMEMQTT_LOOP_MQTT_H
#include <aqhome/api.h>
#include "./aqhome_mqtt.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/msg.h>
AQHOME_API GWEN_MSG_ENDPOINT *AQH_IpcNodeEndpoint_new(const char *name, int groupId);
void AqHomeMqttLog_ReadAndHandleMqttMessages(AQHOME_MQTT *aqh);
int AqHomeMqttLog_SendPing(AQHOME_MQTT *aqh);
AQHOME_API int AQH_IpcNodeEndpointMgr_LoopOnce(GWEN_MSG_ENDPOINT_MGR *emgr);
#endif

279
apps/aqhome-mqttlog/main.c Normal file
View File

@@ -0,0 +1,279 @@
/****************************************************************************
* 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 "./init.h"
#include "./fini.h"
#include "./loop.h"
#include "./loop_mqtt.h"
#include "aqhome/aqhome.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/db.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/cgui.h>
#include <gwenhywfar/text.h>
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
#define AQHOME_MQTTLOG_PING_INTERVAL 120
#define AQHOME_MQTTLOG_SAVE_INTERVAL 60
#define FULL_DEBUG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _serve(AQHOME_MQTT *aqh);
#ifdef HAVE_SIGNAL_H
static int _setSignalHandlers(void);
static int _setupSigAction(struct sigaction *sa, int sig);
static void _signalHandler(int s);
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT;
#endif
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
AQHOME_MQTT *aqh;
GWEN_DB_NODE *dbArgs;
int rv;
GWEN_GUI *gui;
const char *s;
rv=GWEN_Init();
if (rv) {
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
return 2;
}
GWEN_Logger_Open(0, "aqhome-mqttlog", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
rv=AQH_Init();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
aqh=AqHomeMqtt_new();
rv=AqHomeMqtt_Init(aqh, argc, argv);
if (rv<0) {
if (rv==GWEN_ERROR_CLOSE)
return 1;
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
dbArgs=AqHomeMqtt_GetDbArgs(aqh);
gui=GWEN_Gui_CGui_new();
s=GWEN_DB_GetCharValue(dbArgs, "charset", 0, NULL);
if (s && *s)
GWEN_Gui_SetCharSet(gui, s);
GWEN_Gui_SetGui(gui);
_serve(aqh);
AqHomeMqtt_Fini(aqh);
AqHomeMqtt_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _serve(AQHOME_MQTT *aqh)
{
int rv;
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);
rv=_setSignalHandlers();
if (rv<0) {
DBG_ERROR(NULL, "Error setting signal handlers (%d)", rv);
return;
}
timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
lastPingSendTime=time(NULL);
while(!stopService) {
DBG_DEBUG(NULL, "Next loop");
AqHomeMqttLog_Loop(aqh, 2000);
if (timeout) {
time_t now;
now=time(NULL);
if ((now-startTime)>timeout) {
DBG_ERROR(NULL, "Timeout, stopping service");
break;
}
}
if (1){
time_t now;
now=time(NULL);
if (now-lastPingSendTime>AQHOME_MQTTLOG_PING_INTERVAL) {
rv=AqHomeMqttLog_SendPing(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error sending PING");
}
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");
}
}
int _setSignalHandlers(void)
{
#ifdef HAVE_SIGNAL_H
int rv;
rv=_setupSigAction(&saINT, SIGINT);
if (rv)
return rv;
rv=_setupSigAction(&saTERM, SIGTERM);
if (rv)
return rv;
rv=_setupSigAction(&saHUP, SIGHUP);
if (rv)
return rv;
# ifdef SIGTSTP
rv=_setupSigAction(&saTSTP, SIGTSTP);
if (rv)
return rv;
# endif
# ifdef SIGCONT
rv=_setupSigAction(&saCONT, SIGCONT);
if (rv)
return rv;
# endif
#endif
return 0;
}
int _setupSigAction(struct sigaction *sa, int sig)
{
sa->sa_handler=_signalHandler;
sigemptyset(&sa->sa_mask);
sa->sa_flags=0;
if (sigaction(sig, sa, 0)) {
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
return GWEN_ERROR_IO;
}
return 0;
}
void _signalHandler(int s)
{
switch(s) {
case SIGINT:
case SIGTERM:
case SIGHUP:
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
stopService=1;
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}

View File

@@ -0,0 +1,81 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhmqtt_types" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
device.t2d
value.t2d
topic.t2d
translation.t2d
</setVar>
<setVar name="local/built_sources" >
device.c
value.c
topic.c
translation.c
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
device.h
device_p.h
value.h
value_p.h
topic.h
topic_p.h
translation.h
translation_p.h
</setVar>
<headers dist="true" >
</headers>
<sources>
$(local/typefiles)
</sources>
<useTargets>
</useTargets>
<libraries>
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,75 @@
<?xml?>
<tm2>
<type id="AQHMQTT_DEVICE" type="pointer">
<descr>
This object and its objects are used to store registered devices and definitions for possible new devices.
</descr>
<lang id="c">
<identifier>AQHMQTT_DEVICE</identifier>
<prefix>AQHMQTT_Device</prefix>
<baseFileName>device</baseFileName>
<flags>
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">aqhome-mqttlog/types/topic.h</header>
</headers>
<inlines>
</inlines>
</lang>
<enums>
</enums>
<members>
<member name="id" type="char_ptr" maxlen="128">
<descr>Only set for registered devices</descr>
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own with_getbymember</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="driver" type="char_ptr" maxlen="64">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="topicList" type="AQHMQTT_TOPIC_LIST" >
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own noconst</flags>
<getflags>none</getflags>
<setflags>none</setflags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,114 @@
<?xml?>
<tm2>
<type id="AQHMQTT_TOPIC" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQHMQTT_TOPIC</identifier>
<prefix>AQHMQTT_Topic</prefix>
<baseFileName>topic</baseFileName>
<flags>
with_list1
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">aqhome-mqttlog/types/value.h</header>
</headers>
<inlines>
</inlines>
</lang>
<enums>
<enum id="AQHMQTT_TOPIC_TYPE" prefix="AQHMQTT_TopicType_">
<item name="num" value="0">
<descr>numeric type</descr>
</item>
<item name="json" >
<descr>JSON type</descr>
</item>
</enum>
<enum id="AQHMQTT_TOPIC_DIR" prefix="AQHMQTT_TopicDir_">
<item name="in" value="0"/>
<item name="out" />
</enum>
</enums>
<members>
<member name="topic" type="char_ptr" maxlen="128">
<descr>Only set for registered devices</descr>
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="topicType" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="direction" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<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>
<access>public</access>
<flags>own</flags>
</member>
<member name="beforeId" type="char_ptr" maxlen="128">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="afterId" type="char_ptr" maxlen="128">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="valueList" type="AQHMQTT_VALUE_LIST" >
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<getflags>none</getflags>
<setflags>none</setflags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,55 @@
<?xml?>
<tm2>
<type id="AQHMQTT_TRANSLATION" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQHMQTT_TRANSLATION</identifier>
<prefix>AQHMQTT_Translation</prefix>
<baseFileName>translation</baseFileName>
<flags>
with_db
with_list1
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
</headers>
<inlines>
</inlines>
</lang>
<enums>
</enums>
<members>
<member name="aqhValue" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="driverValue" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,92 @@
<?xml?>
<tm2>
<type id="AQHMQTT_VALUE" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQHMQTT_VALUE</identifier>
<prefix>AQHMQTT_Value</prefix>
<baseFileName>value</baseFileName>
<flags>
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">aqhome-mqttlog/types/translation.h</header>
</headers>
<inlines>
</inlines>
</lang>
<enums>
<enum id="AQHMQTT_VALUE_TYPE" prefix="AQHMQTT_ValueType_">
<item name="sensor" value="0">
<descr>sensor</descr>
</item>
<item name="actor" >
<descr>actor</descr>
</item>
</enum>
</enums>
<members>
<member name="name" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="valueType" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="valueUnits" type="char_ptr" maxlen="32">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="path" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="translationList" type="AQHMQTT_TRANSLATION_LIST" >
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<getflags>none</getflags>
<setflags>none</setflags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,467 @@
/****************************************************************************
* 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;
}
}

View File

@@ -0,0 +1,26 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOME_MQTTLOG_XMLREAD_H
#define AQHOME_MQTTLOG_XMLREAD_H
#include "aqhome-mqttlog/aqhome_mqtt.h"
#include "aqhome-mqttlog/types/device.h"
AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDataDeviceFiles(AQHOME_MQTT *aqh);
AQHMQTT_DEVICE_LIST *AqHomeMqttLog_ReadDeviceFile(AQHOME_MQTT *aqh, const char *sFilename);
#endif

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

95
apps/aqhome-nodes/0BUILD Normal file
View File

@@ -0,0 +1,95 @@
<?xml?>
<gwbuild>
<target type="Program" name="aqhome-nodes" install="$(sbindir)" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="true" >
aqhomed.h
aqhomed_p.h
init.h
fini.h
loop.h
loop_broker.h
loop_tty.h
loop_tty_ipc.h
loop_tty_broker.h
loop_ipc.h
db.h
tty_log.h
devicesread.h
devicesdump.h
r_setdata.h
</headers>
<sources>
$(local/typefiles)
main.c
aqhomed.c
init.c
fini.c
loop.c
loop_broker.c
loop_tty.c
loop_tty_ipc.c
loop_tty_broker.c
loop_ipc.c
db.c
tty_log.c
devicesread.c
devicesdump.c
r_setdata.c
</sources>
<useTargets>
aqhnodes_types
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
types
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

221
apps/aqhome-nodes/aqhomed.c Normal file
View File

@@ -0,0 +1,221 @@
/****************************************************************************
* 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 "./aqhomed_p.h"
#include "./tty_log.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHOMED *AqHomed_new(void)
{
AQHOMED *aqh;
GWEN_NEW_OBJECT(AQHOMED, aqh);
aqh->rootEndpoint=GWEN_MsgEndpoint_new("root", 0);
aqh->nodeDb=AQH_NodeDb_new();
aqh->requestTree=GWEN_MsgRequest_new();
return aqh;
}
void AqHomed_free(AQHOMED *aqh)
{
if (aqh) {
GWEN_MsgRequest_free(aqh->requestTree);
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
aqh->rootEndpoint=NULL;
aqh->ttyEndpoint=NULL;
aqh->ipcdEndpoint=NULL;
aqh->brokerEndpoint=NULL;
GWEN_DB_Group_free(aqh->dbArgs);
AQH_NodeDb_free(aqh->nodeDb);
aqh->dbArgs=NULL;
free(aqh->logFile);
free(aqh->pidFile);
free(aqh->dbFile);
GWEN_FREE_OBJECT(aqh);
}
}
GWEN_MSG_ENDPOINT *AqHomed_GetTtyEndpoint(const AQHOMED *aqh)
{
return aqh?aqh->ttyEndpoint:NULL;
}
GWEN_MSG_ENDPOINT *AqHomed_GetIpcdEndpoint(const AQHOMED *aqh)
{
return aqh?aqh->ipcdEndpoint:NULL;
}
GWEN_MSG_ENDPOINT *AqHomed_GetBrokerEndpoint(const AQHOMED *aqh)
{
return aqh?aqh->brokerEndpoint:NULL;
}
GWEN_DB_NODE *AqHomed_GetDbArgs(const AQHOMED *aqh)
{
return aqh?aqh->dbArgs:NULL;
}
const char *AqHomed_GetLogFile(const AQHOMED *aqh)
{
return aqh?aqh->logFile:NULL;
}
void AqHomed_SetLogFile(AQHOMED *aqh, const char *s)
{
if (aqh) {
free(aqh->logFile);
aqh->logFile=s?strdup(s):NULL;
}
}
const char *AqHomed_GetPidFile(const AQHOMED *aqh)
{
return aqh?aqh->pidFile:NULL;
}
void AqHomed_SetPidFile(AQHOMED *aqh, const char *s)
{
if (aqh) {
free(aqh->pidFile);
aqh->pidFile=s?strdup(s):NULL;
}
}
const char *AqHomed_GetDbFile(const AQHOMED *aqh)
{
return aqh?aqh->dbFile:NULL;
}
void AqHomed_SetDbFile(AQHOMED *aqh, const char *s)
{
if (aqh) {
free(aqh->dbFile);
aqh->dbFile=s?strdup(s):NULL;
}
}
int AqHomed_GetTimeout(const AQHOMED *aqh)
{
return aqh?aqh->timeout:0;
}
GWEN_MSG_REQUEST *AqHomed_GetRequestTree(const AQHOMED *aqh)
{
return aqh?aqh->requestTree:NULL;
}
void AqHomed_AddRequestToTree(AQHOMED *aqh, GWEN_MSG_REQUEST *rq)
{
if (aqh && rq)
GWEN_MsgRequest_Tree2_AddChild(aqh->requestTree, rq);
}
const AQHNODE_DEVICE_LIST *AqHomed_GetDeviceDefList(const AQHOMED *aqh)
{
return aqh?aqh->deviceDefList:NULL;
}
const AQHNODE_DEVICE *AqHomed_FindDeviceDef(const AQHOMED *aqh, uint32_t manufacturer, uint16_t deviceType, uint16_t deviceVersion)
{
if (aqh && aqh->deviceDefList) {
const AQHNODE_DEVICE *device;
device=AQHNODE_Device_List_First(aqh->deviceDefList);
while(device) {
if (AQHNODE_Device_GetManufacturer(device)==manufacturer &&
AQHNODE_Device_GetDeviceType(device)==deviceType &&
AQHNODE_Device_GetDeviceVersion(device)==(deviceVersion & 0xff00))
return device;
device=AQHNODE_Device_List_Next(device);
}
}
return NULL;
}
const AQHNODE_DEVICE *AqHomed_GetDeviceDefByName(const AQHOMED *aqh, const char *name)
{
if (aqh && aqh->deviceDefList && name)
return AQHNODE_Device_List_GetByName(aqh->deviceDefList, name);
return NULL;
}

View File

@@ -0,0 +1,59 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_H
#define AQHOMED_H
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/db.h>
#include <gwenhywfar/request.h>
#define AQHOME_ENDPOINTGROUP_NODE 1
#define AQHOME_ENDPOINTGROUP_IPC 2
#define AQHOME_ENDPOINTGROUP_MQTT 4
typedef struct AQHOMED AQHOMED;
#include "aqhome-nodes/types/device.h"
AQHOMED *AqHomed_new(void);
void AqHomed_free(AQHOMED *aqh);
GWEN_MSG_ENDPOINT *AqHomed_GetTtyEndpoint(const AQHOMED *aqh);
GWEN_MSG_ENDPOINT *AqHomed_GetIpcdEndpoint(const AQHOMED *aqh);
GWEN_MSG_ENDPOINT *AqHomed_GetBrokerEndpoint(const AQHOMED *aqh);
GWEN_DB_NODE *AqHomed_GetDbArgs(const AQHOMED *aqh);
const char *AqHomed_GetLogFile(const AQHOMED *aqh);
void AqHomed_SetLogFile(AQHOMED *aqh, const char *s);
const char *AqHomed_GetPidFile(const AQHOMED *aqh);
void AqHomed_SetPidFile(AQHOMED *aqh, const char *s);
const char *AqHomed_GetDbFile(const AQHOMED *aqh);
void AqHomed_SetDbFile(AQHOMED *aqh, const char *s);
int AqHomed_GetTimeout(const AQHOMED *aqh);
GWEN_MSG_REQUEST *AqHomed_GetRequestTree(const AQHOMED *aqh);
void AqHomed_AddRequestToTree(AQHOMED *aqh, GWEN_MSG_REQUEST *rq);
const AQHNODE_DEVICE_LIST *AqHomed_GetDeviceDefList(const AQHOMED *aqh);
const AQHNODE_DEVICE *AqHomed_FindDeviceDef(const AQHOMED *aqh, uint32_t manufacturer, uint16_t deviceType, uint16_t deviceVersion);
const AQHNODE_DEVICE *AqHomed_GetDeviceDefByName(const AQHOMED *aqh, const char *name);
#endif

View File

@@ -0,0 +1,53 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_P_H
#define AQHOMED_P_H
#include "./aqhomed.h"
#include "aqhome/nodes/nodedb.h"
/* default values */
#define AQHOMED_DEFAULT_NODEADDR 240
#define AQHOMED_DEFAULT_PIDFILE "/var/run/aqhomed-node.pid"
#define AQHOMED_DEFAULT_DEVICE "/dev/ttyUSB0"
#define AQHOMED_DEFAULT_IPC_PORT 45454
#define AQHOMED_DEFAULT_BROKER_PORT 1899
#define AQHOMED_DEFAULT_BROKER_CLIENTID "nodes"
struct AQHOMED {
GWEN_MSG_ENDPOINT *rootEndpoint;
GWEN_MSG_ENDPOINT *ttyEndpoint;
GWEN_MSG_ENDPOINT *ipcdEndpoint;
GWEN_MSG_ENDPOINT *brokerEndpoint;
AQH_NODE_DB *nodeDb;
AQHNODE_DEVICE_LIST *deviceDefList;
GWEN_DB_NODE *dbArgs;
char *dbFile;
char *logFile;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
int nodeAddress;
GWEN_MSG_REQUEST *requestTree;
};
#endif

442
apps/aqhome-nodes/db.c Normal file
View File

@@ -0,0 +1,442 @@
/****************************************************************************
* 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 "./db.h"
#include "./aqhomed_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_sendstats.h"
#include "aqhome/msg/msg_recvstats.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_value3.h"
#include "aqhome/msg/msg_needaddr.h"
#include "aqhome/msg/msg_claimaddr.h"
#include "aqhome/msg/msg_haveaddr.h"
#include "aqhome/msg/msg_device.h"
#include "aqhome/msg/msg_flashready.h"
#include "aqhome/data/value.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleMsgValue2(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgValue3(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgNeedAddress(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgClaimAddress(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgHaveAddress(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgComSendStat(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgComRecvStat(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgDevice(AQHOMED *aqh, const GWEN_MSG *msg);
static void _handleMsgFlashReady(AQHOMED *aqh, const GWEN_MSG *msg);
static AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQHOMED *aqh, const GWEN_MSG *msg, uint32_t uid);
static void _updateTimestampLastChange(AQH_NODE_INFO *ni);
static void _assignDeviceId(AQHOMED *aqh, AQH_NODE_INFO *ni, uint32_t uid);
static void _announceNodeValues(AQHOMED *aqh, const AQH_NODE_INFO *ni);
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
static void _announceValue(AQHOMED *aqh, uint32_t uid, const AQHNODE_VALUE *v);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_NodeMsgToDb(AQHOMED *aqh, const GWEN_MSG *msg)
{
int msgIsValid;
uint8_t msgType;
DBG_INFO(AQH_LOGDOMAIN,
" - msg %d (%s) from %d to %d",
AQH_NodeMsg_GetMsgType(msg),
AQH_NodeMsg_MsgTypeToChar(AQH_NodeMsg_GetMsgType(msg)),
AQH_NodeMsg_GetSourceAddress(msg),
AQH_NodeMsg_GetDestAddress(msg));
msgIsValid=(AQH_NodeMsg_IsChecksumValid(msg) && AQH_NodeMsg_IsMsgComplete(msg));
msgType=AQH_NodeMsg_GetMsgType(msg);
if (msgIsValid) {
switch(msgType) {
case AQH_MSG_TYPE_COMSENDSTATS: _handleMsgComSendStat(aqh, msg); break;
case AQH_MSG_TYPE_COMRECVSTATS: _handleMsgComRecvStat(aqh, msg); break;
case AQH_MSG_TYPE_VALUE2: _handleMsgValue2(aqh, msg); break;
case AQH_MSG_TYPE_VALUE_REPORT: _handleMsgValue3(aqh, msg); break;
case AQH_MSG_TYPE_NEED_ADDRESS: _handleMsgNeedAddress(aqh, msg); break;
case AQH_MSG_TYPE_CLAIM_ADDRESS: _handleMsgClaimAddress(aqh, msg); break;
case AQH_MSG_TYPE_HAVE_ADDRESS: _handleMsgHaveAddress(aqh, msg); break;
case AQH_MSG_TYPE_DEVICE: _handleMsgDevice(aqh, msg); break;
case AQH_MSG_TYPE_FLASH_READY: _handleMsgFlashReady(aqh, msg); break;
default: break;
}
}
}
void AqHomed_WriteNodeDb(AQHOMED *aqh)
{
if (aqh->dbFile) {
GWEN_DB_NODE *dbNodeDb;
AQH_NodeDb_ClearModified(aqh->nodeDb);
dbNodeDb=GWEN_DB_Group_new("nodeDb");
AQH_NodeDb_toDb(aqh->nodeDb, dbNodeDb);
GWEN_DB_WriteFile(dbNodeDb, aqh->dbFile, GWEN_DB_FLAGS_DEFAULT);
GWEN_DB_Group_free(dbNodeDb);
}
}
void _handleMsgValue2(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_Value2Msg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgValue3(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_Value3Msg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgNeedAddress(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_NeedAddrMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgClaimAddress(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_ClaimAddrMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgHaveAddress(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_HaveAddrMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgComSendStat(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_SendStatsMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMsg_GetPacketsOut(msg));
AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMsg_GetCollisions(msg));
AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMsg_GetBusyErrors(msg));
AQH_NodeDb_SetModified(aqh->nodeDb);
_updateTimestampLastChange(ni);
}
void _handleMsgComRecvStat(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_RecvStatsMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
AQH_NodeInfo_SetStatsPacketsIn(ni, AQH_RecvStatsMsg_GetPacketsIn(msg));
AQH_NodeInfo_SetStatsCrcErrors(ni, AQH_RecvStatsMsg_GetCrcErrors(msg));
AQH_NodeInfo_SetStatsIoErrors(ni, AQH_RecvStatsMsg_GetIoErrors(msg));
AQH_NodeDb_SetModified(aqh->nodeDb);
_updateTimestampLastChange(ni);
}
void _handleMsgDevice(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_DeviceMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni) {
const char *s;
AQH_NodeInfo_SetManufacturer(ni, AQH_DeviceMsg_GetManufacturer(msg));
AQH_NodeInfo_SetDeviceType(ni, AQH_DeviceMsg_GetDeviceType(msg));
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_DeviceMsg_GetDeviceVersion(msg)<<8)+AQH_DeviceMsg_GetDeviceRevision(msg));
AQH_NodeInfo_SetFirmwareVersion(ni,
(AQH_DeviceMsg_GetFirmwareVariant(msg)<<24) |
(AQH_DeviceMsg_GetFirmwareVersionMajor(msg)<<16) |
(AQH_DeviceMsg_GetFirmwareVersionMinor(msg)<<8) |
AQH_DeviceMsg_GetFirmwareVersionPatchlevel(msg));
s=AQH_NodeInfo_GetDeviceId(ni);
if (!(s && *s))
_assignDeviceId(aqh, ni, uid);
_updateTimestampLastChange(ni);
AQH_NodeDb_SetModified(aqh->nodeDb);
if (uid!=0x00000000L && uid!=0xffffffff)
_announceNodeValues(aqh, ni);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _announceNodeValues(AQHOMED *aqh, const AQH_NODE_INFO *ni)
{
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(ni);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AqHomed_GetDeviceDefByName(aqh, devName);
if (devInfo) {
const AQHNODE_VALUE_LIST *valueList;
valueList=AQHNODE_Device_GetValueList(devInfo);
if (valueList) {
const AQHNODE_VALUE *v;
v=AQHNODE_Value_List_First(valueList);
while(v) {
DBG_INFO(NULL, "Announcing value \"%08x/%s\" (%d=%s)",
AQH_NodeInfo_GetUid(ni), AQHNODE_Value_GetName(v),
AQHNODE_Value_GetModality(v),
AQH_ValueModality_toString(AQHNODE_Value_GetModality(v)));
_announceValue(aqh, AQH_NodeInfo_GetUid(ni), v);
v=AQHNODE_Value_List_Next(v);
}
}
}
else {
DBG_INFO(NULL, "Node type \"%s\" not in database", devName);
}
}
else {
DBG_INFO(NULL, "Node type not in database");
}
}
void _setDeviceName(AQH_VALUE *value, uint32_t uid)
{
GWEN_BUFFER *buf;
buf=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Buffer_AppendArgs(buf, "%08x", uid);
AQH_Value_SetDeviceName(value, GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
}
void _announceValue(AQHOMED *aqh, uint32_t uid, const AQHNODE_VALUE *v)
{
AQH_VALUE *value;
GWEN_MSG *msg;
value=AQH_Value_new();
_setDeviceName(value, uid);
AQH_Value_SetDriver(value, "nodes");
AQH_Value_SetName(value, AQHNODE_Value_GetName(v));
AQH_Value_SetValueUnits(value, AQHNODE_Value_GetValueUnits(v));
AQH_Value_SetValueType(value, AQHNODE_Value_GetValueType(v));
AQH_Value_SetModality(value, AQHNODE_Value_GetModality(v));
msg=AQH_ValuesDataIpcMsg_newForOneValue(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
GWEN_MsgEndpoint_GetNextMessageId(aqh->brokerEndpoint), 0,
0, value);
GWEN_MsgEndpoint_AddSendMessage(aqh->brokerEndpoint, msg);
AQH_Value_free(value);
}
void _handleMsgFlashReady(AQHOMED *aqh, const GWEN_MSG *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_FlashReadyMsg_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
if (ni) {
const char *s;
AQH_NodeInfo_SetManufacturer(ni, AQH_FlashReadyMsg_GetManufacturer(msg));
AQH_NodeInfo_SetDeviceType(ni, AQH_FlashReadyMsg_GetDeviceType(msg));
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_FlashReadyMsg_GetDeviceVersion(msg)<<8)+AQH_FlashReadyMsg_GetDeviceRevision(msg));
AQH_NodeInfo_SetFirmwareVersion(ni,
(AQH_FlashReadyMsg_GetFirmwareVariant(msg)<<24) |
(AQH_FlashReadyMsg_GetFirmwareVersionMajor(msg)<<16) |
(AQH_FlashReadyMsg_GetFirmwareVersionMinor(msg)<<8) |
AQH_FlashReadyMsg_GetFirmwareVersionPatchlevel(msg));
s=AQH_NodeInfo_GetDeviceId(ni);
if (!(s && *s))
_assignDeviceId(aqh, ni, uid);
_updateTimestampLastChange(ni);
AQH_NodeDb_SetModified(aqh->nodeDb);
if (uid!=0x00000000L && uid!=0xffffffff)
_announceNodeValues(aqh, ni);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQHOMED *aqh, const GWEN_MSG *msg, uint32_t uid)
{
uint8_t busAddr;
AQH_NODE_INFO *ni;
busAddr=AQH_NodeMsg_GetSourceAddress(msg);
ni=AQH_NodeDb_GetNodeInfoByUid(aqh->nodeDb, uid);
if (ni) {
uint8_t storedBusAddr;
storedBusAddr=AQH_NodeInfo_GetBusAddress(ni);
if (busAddr!=0 && storedBusAddr!=busAddr) {
DBG_INFO(AQH_LOGDOMAIN, "Changed busaddr for %08x from %02x to %02x", uid, storedBusAddr, busAddr);
AQH_NodeInfo_SetBusAddress(ni, busAddr);
_updateTimestampLastChange(ni);
AQH_NodeDb_SetModified(aqh->nodeDb);
}
}
else {
int rv;
ni=AQH_NodeInfo_new();
AQH_NodeInfo_SetBusAddress(ni, busAddr);
AQH_NodeInfo_SetUid(ni, uid);
_updateTimestampLastChange(ni);
rv=AQH_NodeDb_AddNodeInfo(aqh->nodeDb, ni);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
AQH_NodeInfo_free(ni);
return NULL;
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Added node %08x (%02x)", uid, busAddr);
}
}
return ni;
}
void _assignDeviceId(AQHOMED *aqh, AQH_NODE_INFO *ni, uint32_t uid)
{
const AQHNODE_DEVICE *dev;
dev=AqHomed_FindDeviceDef(aqh,
AQH_NodeInfo_GetManufacturer(ni),
AQH_NodeInfo_GetDeviceType(ni),
AQH_NodeInfo_GetDeviceVersion(ni));
if (dev==NULL) {
DBG_ERROR(NULL,
"Unknown NODE device encountered (%08x, %04x, %04x)",
AQH_NodeInfo_GetManufacturer(ni),
AQH_NodeInfo_GetDeviceType(ni),
AQH_NodeInfo_GetDeviceVersion(ni));
}
else {
const char *s;
s=AQHNODE_Device_GetName(dev);
DBG_ERROR(NULL, "Found device \"%s\" (%08x)", s?s:"<no name>", uid);
AQH_NodeInfo_SetDeviceId(ni, s);
}
}
void _updateTimestampLastChange(AQH_NODE_INFO *ni)
{
GWEN_TIMESTAMP *t;
t=GWEN_Timestamp_NowInLocalTime();
AQH_NodeInfo_SetTimestampLastChange(ni, t);
GWEN_Timestamp_free(t);
}

25
apps/aqhome-nodes/db.h Normal file
View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_DB_H
#define AQHOMED_DB_H
#include "./aqhomed.h"
void AqHomed_NodeMsgToDb(AQHOMED *aqh, const GWEN_MSG *msg);
void AqHomed_WriteNodeDb(AQHOMED *aqh);
#endif

View File

@@ -0,0 +1,128 @@
/****************************************************************************
* 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 "./devicesdump.h"
#include "./aqhomed_p.h"
#include "aqhome/aqhome.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _dumpDevice(const AQHNODE_DEVICE *dev, GWEN_BUFFER *dbuf, int indent);
static void _dumpValue(const AQHNODE_VALUE *value, GWEN_BUFFER *dbuf, int indent);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeNodes_DumpDevices(const AQHNODE_DEVICE_LIST *devList, GWEN_BUFFER *dbuf)
{
if (devList && AQHNODE_Device_List_GetCount(devList)) {
const AQHNODE_DEVICE *dev;
GWEN_Buffer_AppendString(dbuf, "Devices:\n");
dev=AQHNODE_Device_List_First(devList);
while(dev) {
_dumpDevice(dev, dbuf, 2);
dev=AQHNODE_Device_List_Next(dev);
}
}
}
void _dumpDevice(const AQHNODE_DEVICE *dev, GWEN_BUFFER *dbuf, int indent)
{
const char *name;
const char *driver;
uint32_t manufacturer;
uint16_t deviceType;
uint16_t deviceVersion;
const AQHNODE_VALUE_LIST *valueList;
name=AQHNODE_Device_GetName(dev);
driver=AQHNODE_Device_GetDriver(dev);
manufacturer=AQHNODE_Device_GetManufacturer(dev);
deviceType=AQHNODE_Device_GetDeviceType(dev);
deviceVersion=AQHNODE_Device_GetDeviceVersion(dev);
GWEN_Buffer_FillWithBytes(dbuf, ' ', indent);
GWEN_Buffer_AppendArgs(dbuf, "Device: %s (%s, %08x, %04x, %04x)\n",
name?name:"<empty name>",
driver?driver:"<empty driver>",
manufacturer,
deviceType,
deviceVersion);
valueList=AQHNODE_Device_GetValueList(dev);
if (valueList && AQHNODE_Value_List_GetCount(valueList)) {
const AQHNODE_VALUE *value;
value=AQHNODE_Value_List_First(valueList);
while(value) {
_dumpValue(value, dbuf, indent+2);
value=AQHNODE_Value_List_Next(value);
}
}
}
void _dumpValue(const AQHNODE_VALUE *value, GWEN_BUFFER *dbuf, int indent)
{
int id;
const char *name;
const char *descr;
int valueType;
int dataType;
int modality;
const char *units;
int denom;
id=AQHNODE_Value_GetId(value);
name=AQHNODE_Value_GetName(value);
descr=AQHNODE_Value_GetDescription(value);
valueType=AQHNODE_Value_GetValueType(value);
dataType=AQHNODE_Value_GetDataType(value);
modality=AQHNODE_Value_GetModality(value);
units=AQHNODE_Value_GetValueUnits(value);
denom=AQHNODE_Value_GetDenom(value);
GWEN_Buffer_FillWithBytes(dbuf, ' ', indent);
GWEN_Buffer_AppendArgs(dbuf, "Value: %d[%02x] (%s, %s, %s, %s, %s, %s, %d)\n",
id, id,
name?name:"<empty name>",
AQH_ValueType_toString(valueType),
AQH_ValueDataType_toString(dataType),
AQH_ValueModality_toString(modality),
units?units:"<empty units>",
descr?descr:"<empty descr>",
denom);
}

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_NODES_DEVICESDUMP_H
#define AQHOME_NODES_DEVICESDUMP_H
#include "./aqhomed.h"
#include "aqhome-nodes/types/device.h"
void AqHomeNodes_DumpDevices(const AQHNODE_DEVICE_LIST *devList, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,397 @@
/****************************************************************************
* 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 "./devicesread.h"
#include "./aqhomed_p.h"
#include "aqhome/aqhome.h"
#include <gwenhywfar/directory.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/stringlist.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _readDeviceFileToList(const char *sFilename, AQHNODE_DEVICE_LIST *deviceList);
static AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl);
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHNODE_DEVICE_LIST *deviceList);
static AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
static AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode);
static AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
static int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
static int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
static int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHNODE_DEVICE_LIST *AqHomeNodes_ReadDeviceFile(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 {
AQHNODE_DEVICE_LIST *deviceList;
deviceList=AQHNODE_Device_List_new();
rv=_readDeviceFileToList(sFilename, deviceList);
if (rv<0) {
DBG_ERROR(NULL, "File \"%s\" not found", sFilename);
AQHNODE_Device_List_free(deviceList);
return NULL;
}
if (AQHNODE_Device_List_GetCount(deviceList)<1) {
AQHNODE_Device_List_free(deviceList);
return NULL;
}
return deviceList;
}
}
AQHNODE_DEVICE_LIST *AqHomeNodes_ReadDataDeviceFiles()
{
GWEN_STRINGLIST *sl;
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/nodes", "*.xml");
if (sl) {
AQHNODE_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;
}
}
AQHNODE_DEVICE_LIST *_readDeviceFiles(const GWEN_STRINGLIST *sl)
{
GWEN_STRINGLISTENTRY *se;
AQHNODE_DEVICE_LIST *deviceList;
deviceList=AQHNODE_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 (AQHNODE_Device_List_GetCount(deviceList)<1) {
AQHNODE_Device_List_free(deviceList);
return NULL;
}
return deviceList;
}
int _readDeviceFileToList(const char *sFilename, AQHNODE_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, AQHNODE_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, "nodes")==0) {
AQHNODE_DEVICE *device;
device=_readXmlDevice(deviceNode);
if (device==NULL) {
DBG_INFO(NULL, "Error reading device from XML");
return GWEN_ERROR_BAD_DATA;
}
else {
const char *sDeviceName;
sDeviceName=AQHNODE_Device_GetName(device);
if (sDeviceName && *sDeviceName) {
DBG_ERROR(NULL, "Adding device type \"%s\" to list", sDeviceName?sDeviceName:"<no type name>");
AQHNODE_Device_List_Add(device, deviceList);
}
else {
DBG_ERROR(NULL, "Device with no name, not adding");
AQHNODE_Device_free(device);
}
}
}
else {
DBG_INFO(NULL, "Device is not an NODES 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;
}
}
AQHNODE_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode)
{
AQHNODE_DEVICE *device;
GWEN_XMLNODE *valuesNode;
int rv;
device=AQHNODE_Device_new();
AQHNODE_Device_SetName(device, GWEN_XMLNode_GetProperty(deviceNode, "name", NULL));
AQHNODE_Device_SetDriver(device, GWEN_XMLNode_GetProperty(deviceNode, "driver", NULL));
rv=_readManufacturer(device, deviceNode);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQHNODE_Device_free(device);
return NULL;
}
rv=_readDeviceType(device, deviceNode);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQHNODE_Device_free(device);
return NULL;
}
rv=_readDeviceVersion(device, deviceNode);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQHNODE_Device_free(device);
return NULL;
}
/* read values */
valuesNode=GWEN_XMLNode_FindFirstTag(deviceNode, "values", NULL, NULL);
if (valuesNode) {
AQHNODE_VALUE_LIST *valueList;
valueList=_readXmlValueList(valuesNode);
if (valueList==NULL) {
DBG_INFO(NULL, "here");
AQHNODE_Device_free(device);
return NULL;
}
AQHNODE_Device_SetValueList(device, valueList);
}
else {
DBG_INFO(NULL, "No <values> element");
AQHNODE_Device_free(device);
return NULL;
}
return device;
}
AQHNODE_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *valuesNode)
{
AQHNODE_VALUE_LIST *valueList;
GWEN_XMLNODE *node;
valueList=AQHNODE_Value_List_new();
node=GWEN_XMLNode_FindFirstTag(valuesNode, "value", NULL, NULL);
while(node) {
AQHNODE_VALUE *value;
value=_readXmlValue(node);
if (value)
AQHNODE_Value_List_Add(value, valueList);
else {
DBG_INFO(NULL, "Error reading <value> element");
AQHNODE_Value_List_free(valueList);
return NULL;
}
node=GWEN_XMLNode_FindNextTag(node, "value", NULL, NULL);
}
if (AQHNODE_Value_List_GetCount(valueList)<1) {
DBG_INFO(NULL, "No <value> element in <values> element");
AQHNODE_Value_List_free(valueList);
return NULL;
}
return valueList;
}
AQHNODE_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
{
AQHNODE_VALUE *value;
value=AQHNODE_Value_new();
AQHNODE_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
AQHNODE_Value_SetId(value, GWEN_XMLNode_GetIntProperty(valueNode, "id", 0));
AQHNODE_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
AQHNODE_Value_SetValueType(value, AQH_ValueType_fromString(GWEN_XMLNode_GetProperty(valueNode, "type", NULL)));
AQHNODE_Value_SetDataType(value, AQH_ValueDataType_fromString(GWEN_XMLNode_GetProperty(valueNode, "dataType", NULL)));
AQHNODE_Value_SetModality(value, AQH_ValueModality_fromString(GWEN_XMLNode_GetProperty(valueNode, "modality", NULL)));
AQHNODE_Value_SetDenom(value, GWEN_XMLNode_GetIntProperty(valueNode, "denom", 1));
return value;
}
int _readManufacturer(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
const char *s;
uint32_t v;
s=GWEN_XMLNode_GetCharValue(deviceNode, "manufacturer", NULL);
if (!(s && *s)) {
DBG_ERROR(NULL, "Missing manufacturer");
return GWEN_ERROR_BAD_DATA;
}
if (strlen(s)>4) {
DBG_ERROR(NULL, "Bad manufacturer string (too long): \"%s\"", s);
return GWEN_ERROR_BAD_DATA;
}
v=0;
while(*s) {
v<<=8;
v|=*s & 0xff;
s++;
}
v=((v>>24) & 0x000000ffL) |
((v>>8) & 0x0000ff00L) |
((v<<8) & 0x00ff0000L) |
((v<<24) & 0xff000000L);
AQHNODE_Device_SetManufacturer(device, v);
return 0;
}
int _readDeviceType(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
const char *s;
uint16_t v;
s=GWEN_XMLNode_GetCharValue(deviceNode, "devicetype", NULL);
if (!(s && *s)) {
DBG_ERROR(NULL, "Missing device type");
return GWEN_ERROR_BAD_DATA;
}
if (strlen(s)>2) {
DBG_ERROR(NULL, "Bad devicetype string (too long): \"%s\"", s);
return GWEN_ERROR_BAD_DATA;
}
v=0;
while(*s) {
v<<=8;
v|=*s & 0xff;
s++;
}
AQHNODE_Device_SetDeviceType(device, v);
return 0;
}
int _readDeviceVersion(AQHNODE_DEVICE *device, GWEN_XMLNODE *deviceNode)
{
int v;
v=GWEN_XMLNode_GetIntValue(deviceNode, "deviceversion", -1);
if (v<0) {
DBG_ERROR(NULL, "Missing device version");
return GWEN_ERROR_BAD_DATA;
}
AQHNODE_Device_SetDeviceVersion(device, v<<8);
return 0;
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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_NODES_DEVICESREAD_H
#define AQHOME_NODES_DEVICESREAD_H
#include "./aqhomed.h"
#include "aqhome-nodes/types/device.h"
AQHNODE_DEVICE_LIST *AqHomeNodes_ReadDeviceFile(const char *sFilename);
AQHNODE_DEVICE_LIST *AqHomeNodes_ReadDataDeviceFiles(void);
#endif

89
apps/aqhome-nodes/fini.c Normal file
View File

@@ -0,0 +1,89 @@
/****************************************************************************
* 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 "./fini.h"
#include "./db.h"
#include "./aqhomed_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <unistd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _disconnectTree(GWEN_MSG_ENDPOINT *ep);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_Fini(AQHOMED *aqh)
{
if (aqh) {
if (aqh->rootEndpoint) {
_disconnectTree(aqh->rootEndpoint);
GWEN_MsgEndpoint_Disconnect(aqh->rootEndpoint);
}
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
aqh->rootEndpoint=NULL;
aqh->ttyEndpoint=NULL;
aqh->ipcdEndpoint=NULL;
aqh->brokerEndpoint=NULL;
AqHomed_WriteNodeDb(aqh);
if (aqh->pidFile)
remove(aqh->pidFile);
}
}
void _disconnectTree(GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG_ENDPOINT *epChild;
epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep);
while(epChild) {
_disconnectTree(epChild);
epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild);
} /* while */
GWEN_MsgEndpoint_Disconnect(ep);
}

23
apps/aqhome-nodes/fini.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_FINI_H
#define AQHOMED_FINI_H
#include "./aqhomed.h"
void AqHomed_Fini(AQHOMED *aqh);
#endif

540
apps/aqhome-nodes/init.c Normal file
View File

@@ -0,0 +1,540 @@
/****************************************************************************
* 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 "./init.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./devicesread.h"
#include "./devicesdump.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/endpoint_ipcclient.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <gwenhywfar/endpoint_multilayer.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _setupTty(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
static void _setupIpcd(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
static void _setupBroker(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data);
static void _setupLog(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
static void _setupDb(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
static int _loadDeviceList(AQHOMED *aqh);
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
static int _createPidFile(const char *pidFilename);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int AqHomed_Init(AQHOMED *aqh, int argc, char **argv)
{
GWEN_DB_NODE *dbArgs;
int rv;
const char *s;
dbArgs=GWEN_DB_Group_new("args");
rv=_readArgs(argc, argv, dbArgs);
if (rv<0) {
DBG_ERROR(NULL, "Error reading args (%d)", rv);
return rv;
}
AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile");
aqh->dbArgs=dbArgs;
s=GWEN_DB_GetCharValue(dbArgs, "loglevel", 0, NULL);
if (s && *s) {
GWEN_LOGGER_LEVEL ll;
ll=GWEN_Logger_Name2Level(s);
GWEN_Logger_SetLevel(NULL, ll);
}
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOMED_DEFAULT_PIDFILE);
if (s && *s) {
AqHomed_SetPidFile(aqh, s);
rv=_createPidFile(s);
if (rv<0) {
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
return rv;
}
}
aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
aqh->nodeAddress=GWEN_DB_GetIntValue(dbArgs, "nodeAddress", 0, AQHOMED_DEFAULT_NODEADDR);
rv=_loadDeviceList(aqh);
if (rv<0) {
DBG_ERROR(NULL, "Error loading device list(%d)", rv);
return rv;
}
#if 0
else {
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
AqHomeNodes_DumpDevices(aqh->deviceDefList, dbuf);
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
#endif
_setupDb(aqh, dbArgs);
rv=_setupTty(aqh, dbArgs);
if (rv<0) {
DBG_ERROR(NULL, "Error setting up TTY endpoint (%d)", rv);
return rv;
}
_setupIpcd(aqh, dbArgs);
_setupBroker(aqh, dbArgs);
_setupLog(aqh, dbArgs);
return 0;
}
int _setupTty(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
{
const char *devicePath;
devicePath=GWEN_DB_GetCharValue(dbArgs, "device", 0, AQHOMED_DEFAULT_DEVICE);
if (devicePath && *devicePath) {
GWEN_MSG_ENDPOINT *epTty;
epTty=AQH_TtyEndpoint_new(devicePath, AQHOME_ENDPOINTGROUP_NODE);
if (epTty==NULL) {
DBG_ERROR(NULL, "Error creating endpoint TTY");
return GWEN_ERROR_GENERIC;
}
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, epTty);
aqh->ttyEndpoint=epTty;
}
else {
DBG_ERROR(NULL, "Missing device path");
return GWEN_ERROR_GENERIC;
}
return 0;
}
void _setupIpcd(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
{
const char *tcpAddress;
int tcpPort;
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL);
if (!(tcpAddress && *tcpAddress))
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/nodesAddress", 0, NULL);
tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, -1);
if (tcpPort<0)
tcpPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/nodesPort", 0, AQHOMED_DEFAULT_IPC_PORT);
if (tcpAddress && *tcpAddress && tcpPort) {
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, AQHOME_ENDPOINTGROUP_IPC);
GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh);
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
aqh->ipcdEndpoint=ep;
}
}
void _setupBroker(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
{
const char *brokerAddress;
int brokerPort;
const char *brokerClientId;
brokerAddress=GWEN_DB_GetCharValue(dbArgs, "brokerAddress", 0, NULL);
if (!(brokerAddress && *brokerAddress))
brokerAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, "127.0.0.1");
brokerPort=GWEN_DB_GetIntValue(dbArgs, "brokerPort", 0, -1);
if (brokerPort<0)
brokerPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOMED_DEFAULT_BROKER_PORT);
brokerClientId=GWEN_DB_GetCharValue(dbArgs, "brokerClientId", 0, AQHOMED_DEFAULT_BROKER_CLIENTID);
if (brokerAddress && *brokerAddress && brokerPort) {
GWEN_MSG_ENDPOINT *ep;
GWEN_MSG_ENDPOINT *ipcBaseEndpoint;
int rv;
ep=AQH_ClientIpcEndpoint_new("brokerIpcClient", 0);
ipcBaseEndpoint=AQH_IpcEndpoint_CreateIpcTcpClient(brokerAddress, brokerPort, "brokerPhysEndpoint", 0);
AQH_IpcEndpoint_SetServiceName(ipcBaseEndpoint, brokerClientId);
GWEN_MsgEndpoint_Tree2_AddChild(ep, ipcBaseEndpoint);
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
aqh->brokerEndpoint=ep;
rv=GWEN_MultilayerEndpoint_StartConnect(ep);
if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) {
DBG_ERROR(NULL, "Error connecting to broker server %s:%d (%d), will retry later", brokerAddress, brokerPort, rv);
}
}
}
GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_UNUSED GWEN_MSG_ENDPOINT *ep,
GWEN_SOCKET *sk,
GWEN_UNUSED const GWEN_INETADDRESS *addr,
GWEN_UNUSED void *data)
{
/* AQHOMED *aqh;
*
* aqh=(AQHOMED*) data;
*/
DBG_INFO(NULL, "Incoming IPC connection");
return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, AQHOME_ENDPOINTGROUP_IPC);
}
void _setupLog(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
{
const char *logFile;
logFile=GWEN_DB_GetCharValue(dbArgs, "logfile", 0, NULL);
if (logFile && *logFile)
AqHomed_SetLogFile(aqh, logFile);
}
void _setupDb(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
{
const char *s;
s=GWEN_DB_GetCharValue(dbArgs, "dbfile", 0, NULL);
if (s && *s) {
GWEN_DB_NODE *dbNodeDb;
int rv;
AqHomed_SetDbFile(aqh, s);
dbNodeDb=GWEN_DB_Group_new("dbNodes");
rv=GWEN_DB_ReadFile(dbNodeDb, s, GWEN_DB_FLAGS_DEFAULT|GWEN_PATH_FLAGS_CREATE_GROUP);
if (rv==0) {
AQH_NodeDb_fromDb(aqh->nodeDb, dbNodeDb);
}
GWEN_DB_Group_free(dbNodeDb);
}
}
int _loadDeviceList(AQHOMED *aqh)
{
AQHNODE_DEVICE_LIST *deviceList;
deviceList=AqHomeNodes_ReadDataDeviceFiles();
if (deviceList==NULL) {
DBG_ERROR(NULL, "Error reading device list");
return GWEN_ERROR_GENERIC;
}
aqh->deviceDefList=deviceList;
return 0;
}
int _createPidFile(const char *pidFilename)
{
FILE *f;
int pidfd;
if (remove(pidFilename)==0) {
DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)");
}
#ifdef HAVE_SYS_STAT_H
pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (pidfd < 0) {
DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
f = fdopen(pidfd, "w");
#else /* HAVE_STAT_H */
f=fopen(pidFilename,"w+");
#endif /* HAVE_STAT_H */
/* write pid */
#ifdef HAVE_GETPID
fprintf(f,"%d\n",getpid());
#else
fprintf(f,"-1\n");
#endif
if (fclose(f)) {
DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
return GWEN_ERROR_IO;
}
return 0;
}
int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
{
int rv;
const GWEN_ARGS args[]= {
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"loglevel", /* name */
0, /* minnum */
1, /* maxnum */
"L", /* short option */
"loglevel", /* long option */
I18S("Specify loglevel"), /* short description */
I18S("Specify loglevel") /* long description */
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"cfgdir", /* name */
0, /* minnum */
1, /* maxnum */
"D", /* short option */
"cfgdir", /* long option */
I18S("Specify the configuration folder"),
I18S("Specify the configuration folder")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"charset", /* name */
0, /* minnum */
1, /* maxnum */
0, /* short option */
"charset", /* long option */
I18S("Specify the output character set"), /* short description */
I18S("Specify the output character set") /* long description */
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"device", /* name */
0, /* minnum */
1, /* maxnum */
"d", /* short option */
"device", /* long option */
I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)"),
I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"nodeAddress", /* name */
0, /* minnum */
1, /* maxnum */
"n", /* short option */
"node", /* long option */
I18S("Specify the node address for the AqHome node adaptor (default 240)"),
I18S("Specify the node address for the AqHome node adaptor (default 240)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"logFile", /* name */
0, /* minnum */
1, /* maxnum */
"l", /* short option */
"logfile", /* long option */
I18S("Specify a logfile to log received messages to"),
I18S("Specify a logfile to log received messages to")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"tcpAddress", /* name */
0, /* minnum */
1, /* maxnum */
"t", /* short option */
"tcpaddress", /* long option */
I18S("Specify the TCP address to listen on (disabled if missing)"),
I18S("Specify the TCP address to listen on (disabled if missing)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"tcpPort", /* name */
0, /* minnum */
1, /* maxnum */
"P", /* short option */
"tcpport", /* long option */
I18S("Specify the TCP port to listen on (default: 45454)"),
I18S("Specify the TCP port to listen on (default: 45454)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"brokerAddress", /* name */
0, /* minnum */
1, /* maxnum */
"ba", /* short option */
"brokeraddress", /* long option */
I18S("Specify the address of the broker server to connect to (disabled if missing)"),
I18S("Specify the address of the broker server to connect to (disabled if missing)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"brokerPort", /* name */
0, /* minnum */
1, /* maxnum */
"bp", /* short option */
"brokerport", /* long option */
I18S("Specify the port of the broker server (default: 1899)"),
I18S("Specify the port of the broker server (default: 1899)")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"brokerClientId", /* name */
0, /* minnum */
1, /* maxnum */
NULL, /* short option */
"brokerclientid", /* long option */
I18S("Specify client id for the broker server (default: \"nodes\")"),
I18S("Specify client id for the broker server (default: \"nodes\")")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"dbfile", /* name */
0, /* minnum */
1, /* maxnum */
"db", /* short option */
"dbfile", /* long option */
I18S("Specify DB file to read/write node database"),
I18S("Specify DB file to read/write node database")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Char, /* type */
"pidfile", /* name */
0, /* minnum */
1, /* maxnum */
"p", /* short option */
"pidfile", /* long option */
I18S("Specify the PID file"),
I18S("Specify the PID file")
},
{
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
GWEN_ArgsType_Int, /* type */
"timeout", /* name */
0, /* minnum */
1, /* maxnum */
"T", /* short option */
"timeout", /* long option */
I18S("Specify timeout in second (default: no timeout)"),
I18S("Specify timeout in second (default: no timeout)")
},
{
GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */
GWEN_ArgsType_Int, /* type */
"help", /* name */
0, /* minnum */
0, /* maxnum */
"h", /* short option */
"help",
I18S("Show this help screen."),
I18S("Show this help screen.")
}
};
rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs);
if (rv==GWEN_ARGS_RESULT_ERROR) {
fprintf(stderr, "ERROR: Could not parse arguments main\n");
return GWEN_ERROR_INVALID;
}
else if (rv==GWEN_ARGS_RESULT_HELP) {
GWEN_BUFFER *ubuf;
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
GWEN_Buffer_AppendArgs(ubuf,
I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"),
AQHOME_VERSION_STRING,
argv[0]);
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
fprintf(stderr, "ERROR: Could not create help string\n");
return 1;
}
GWEN_Buffer_AppendString(ubuf, "\n");
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf));
GWEN_Buffer_free(ubuf);
return GWEN_ERROR_CLOSE;
}
return 0;
}

23
apps/aqhome-nodes/init.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_INIT_H
#define AQHOMED_INIT_H
#include "./aqhomed.h"
int AqHomed_Init(AQHOMED *aqh, int argc, char **argv);
#endif

23
apps/aqhome-nodes/ipc.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_IPC_H
#define AQHOMED_LOOP_IPC_H
#include "./aqhomed.h"
void AqHomed_ForwardTtyMsgToIpcClients(AQHOMED *aqh, const GWEN_MSG *msg);
#endif

91
apps/aqhome-nodes/loop.c Normal file
View File

@@ -0,0 +1,91 @@
/****************************************************************************
* 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 "./loop.h"
#include "./loop_tty.h"
#include "./loop_ipc.h"
#include "./loop_broker.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/ipc/requests.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_Loop(AQHOMED *aqh, int timeoutInMsecs)
{
if (aqh) {
GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs);
AqHomed_ReadAndHandleTtyMessages(aqh);
AqHomed_ReadAndHandleIpcMessages(aqh);
AqHomed_ReadAndHandleBrokerMessages(aqh);
AQH_Requests_CheckTimeouts(aqh->requestTree);
AQH_Requests_Cleanup(aqh->requestTree);
#if 0
DBG_ERROR(NULL, "Messages in TTY queue: %d in, %d out",
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetReceivedMessageList(aqh->ttyEndpoint)),
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetSendMessageList(aqh->ttyEndpoint)));
DBG_ERROR(NULL, "Messages in IPC queue: %d in, %d out",
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetReceivedMessageList(aqh->ipcdEndpoint)),
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetSendMessageList(aqh->ipcdEndpoint)));
DBG_ERROR(NULL, "Messages in Broker queue: %d in, %d out",
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetReceivedMessageList(aqh->brokerEndpoint)),
GWEN_Msg_List_GetCount(GWEN_MsgEndpoint_GetSendMessageList(aqh->brokerEndpoint)));
#endif
if (AQH_NodeDb_IsModified(aqh->nodeDb))
AqHomed_WriteNodeDb(aqh);
}
}

23
apps/aqhome-nodes/loop.h Normal file
View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_H
#define AQHOMED_LOOP_H
#include "./aqhomed.h"
void AqHomed_Loop(AQHOMED *aqh, int timeoutInMsecs);
#endif

View File

@@ -0,0 +1,101 @@
/****************************************************************************
* 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 "./loop_broker.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "./r_setdata.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_ping.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/requests.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ReadAndHandleBrokerMessages(AQHOMED *aqh)
{
if (aqh->brokerEndpoint) {
GWEN_MSG_ENDPOINT *epTcp;
GWEN_MSG *msg;
epTcp=aqh->brokerEndpoint;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(epTcp)) ) {
uint16_t code;
code=GWEN_IpcMsg_GetCode(msg);
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC packet %d (%x)", (int) code, code);
if (AQH_Requests_HandleIpcMsg(aqh->requestTree, epTcp, msg)!=GWEN_MSG_REQUEST_RESULT_HANDLED)
_handleIpcMsg(aqh, epTcp, msg);
GWEN_Msg_free(msg);
}
}
}
void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
uint16_t code;
/* exec IPC message */
code=GWEN_IpcMsg_GetCode(msg);
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC packet");
switch(code) {
case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeNodes_HandleSetData(aqh, ep, msg); break;
default: break;
}
}

View File

@@ -0,0 +1,25 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_BROKER_H
#define AQHOMED_LOOP_BROKER_H
#include "./aqhomed.h"
/**
* Handle messageexchange with the broker (aqhome-data).
*/
void AqHomed_ReadAndHandleBrokerMessages(AQHOMED *aqh);
#endif

View File

@@ -0,0 +1,197 @@
/****************************************************************************
* 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 "./loop_ipc.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "./r_setdata.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_value3.h"
#include "aqhome/msg/msg_ping.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/ipc_nodes.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/ipc/nodes/msg_ipc_ping.h"
#include "aqhome/ipc/nodes/msg_ipc_setaccmsggrps.h"
#include "aqhome/ipc/nodes/msg_ipc_getdevices_rsp.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_set.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/requests.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <stdio.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleIpcEndpoint(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep);
static void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ReadAndHandleIpcMessages(AQHOMED *aqh)
{
if (aqh->ipcdEndpoint) {
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
_handleIpcEndpoint(aqh, ep);
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
}
}
void _handleIpcEndpoint(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG *msg;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) {
if (AQH_Requests_HandleIpcMsg(aqh->requestTree, ep, msg)!=GWEN_MSG_REQUEST_RESULT_HANDLED)
_handleIpcMsg(aqh, ep, msg);
GWEN_Msg_free(msg);
}
}
void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
uint16_t code;
/* exec IPC message */
code=GWEN_IpcMsg_GetCode(msg);
DBG_INFO(AQH_LOGDOMAIN, "Received IPC packet (%d, %04x)", code, code);
switch(code) {
case AQH_MSGTYPE_IPC_NODES_PING: _handleIpcMsgPing(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_NODES_SETACCMSGGRPS: _handleIpcMsgSetAccMsgGrps(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_NODES_FORWARD: _handleIpcMsgForward(aqh, ep, msg); break;
case AQH_MSGTYPE_IPC_NODES_GETDEVICES_REQ: _handleIpcMsgGetDevicesReq(aqh, ep, msg); break;
// case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeNodes_HandleNodeSetData(aqh, ep, msg); break;
default: break;
}
}
void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
GWEN_MSG *msgOut;
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC PING message");
msgOut=AQH_PingMsg_new(aqh->nodeAddress, AQH_PingIpcMsg_GetDestAddr(msg), AQH_MSG_TYPE_PING);
GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut);
}
}
void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
uint32_t groups;
DBG_INFO(AQH_LOGDOMAIN, "Received IPC SET_ACCEPTED_MSG_GROUPS message");
groups=AQH_SetAcceptedMsgGroupsIpcMsg_GetMsgGroups(msg);
AQH_IpcEndpoint_SetAcceptedMsgGroups(ep, groups);
// TODO: send response?
}
void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
GWEN_MSG *msgOut;
DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC FORWARD message");
msgOut=AQH_ForwardIpcMsg_GetCopyOfNodeMsg(msg);
if (msgOut)
GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut);
}
}
void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
AQH_NODE_INFO_LIST *nodeInfoList;
DBG_INFO(AQH_LOGDOMAIN, "Received IPC GetDevicesRequest message");
nodeInfoList=AQH_NodeDb_GetAllNodeInfos(aqh->nodeDb);
if (nodeInfoList && AQH_NodeInfo_List_GetCount(nodeInfoList)) {
const AQH_NODE_INFO *ni;
ni=AQH_NodeInfo_List_First(nodeInfoList);
while(ni) {
const AQH_NODE_INFO *niNext;
GWEN_MSG *msgOut;
niNext=AQH_NodeInfo_List_Next(ni);
DBG_INFO(AQH_LOGDOMAIN, "Sending response for node %02x (%08x)", AQH_NodeInfo_GetBusAddress(ni), AQH_NodeInfo_GetUid(ni));
msgOut=AQH_GetDevicesResponseIpcMsg_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
niNext?0:AQH_MSGIPC_GETDEVICES_RSP_FLAGS_LAST, ni);
GWEN_MsgEndpoint_AddSendMessage(ep, msgOut);
ni=niNext;
}
}
else {
GWEN_MSG *msgOut;
DBG_INFO(AQH_LOGDOMAIN, "No nodes");
msgOut=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_NODES_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
AQH_MSG_IPC_ERROR_NODATA);
GWEN_MsgEndpoint_AddSendMessage(ep, msgOut);
}
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_IPC_H
#define AQHOMED_LOOP_IPC_H
#include "./aqhomed.h"
void AqHomed_ReadAndHandleIpcMessages(AQHOMED *aqh);
#endif

View File

@@ -0,0 +1,87 @@
/****************************************************************************
* 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 "./loop_tty.h"
#include "./loop_tty_ipc.h"
#include "./loop_tty_broker.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/ipc/requests.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ReadAndHandleTtyMessages(AQHOMED *aqh)
{
GWEN_MSG *msg;
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(aqh->ttyEndpoint)) ) {
_handleTtyMsg(aqh, msg);
AQH_Requests_HandleTtyMsg(aqh->requestTree, aqh->ttyEndpoint, msg);
GWEN_Msg_free(msg);
}
}
void _handleTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
{
if (aqh->logFile)
AqHomed_LogTtyMsg(aqh, msg);
if (aqh->nodeDb)
AqHomed_NodeMsgToDb(aqh, msg);
if (aqh->ipcdEndpoint)
AqHomed_ForwardTtyMsgToIpcClients(aqh, msg);
if (aqh->brokerEndpoint)
AqHomed_ForwardTtyMsgToBroker(aqh, msg);
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_TTY_H
#define AQHOMED_LOOP_TTY_H
#include "./aqhomed.h"
void AqHomed_ReadAndHandleTtyMessages(AQHOMED *aqh);
#endif

View File

@@ -0,0 +1,234 @@
/****************************************************************************
* 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 "./loop_tty_broker.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_value3.h"
#include "aqhome/msg/msg_sendstats.h"
#include "aqhome/msg/msg_recvstats.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/data/ipc_data.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _processValue3Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _publishInt(AQHOMED *aqh, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v);
static void _publishDouble(AQHOMED *aqh, uint32_t uid, const char *vPath, int vType, const char *vUnits, double v);
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ForwardTtyMsgToBroker(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
if (GWEN_MsgEndpoint_GetState(aqh->brokerEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
DBG_DEBUG(AQH_LOGDOMAIN, "Processing output message");
switch(AQH_NodeMsg_GetMsgType(nodeMsg)) {
case AQH_MSG_TYPE_VALUE_REPORT:
_processValue3Message(aqh, nodeMsg);
break;
case AQH_MSG_TYPE_COMSENDSTATS:
_processSendStatsMessage(aqh, nodeMsg);
break;
case AQH_MSG_TYPE_COMRECVSTATS:
_processRecvStatsMessage(aqh, nodeMsg);
break;
default:
break;
}
}
}
void _processValue3Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
uint32_t uid;
uint8_t valueId;
AQH_NODE_INFO *ni;
double v;
uid=AQH_Value3Msg_GetUid(nodeMsg);
valueId=AQH_Value3Msg_GetValueId(nodeMsg);
v=AQH_Value3Msg_GetValue(nodeMsg);
ni=AQH_NodeDb_GetNodeInfoByUid(aqh->nodeDb, uid);
if (ni) {
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(ni);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AqHomed_GetDeviceDefByName(aqh, devName);
if (devInfo) {
const AQHNODE_VALUE *value;
value=AQHNODE_Value_List_GetById(AQHNODE_Device_GetValueList(devInfo), valueId);
if (value) {
const char *vname;
vname=AQHNODE_Value_GetName(value);
if (vname && *vname)
_publishDouble(aqh, uid, vname, AQHNODE_Value_GetModality(value), AQHNODE_Value_GetValueUnits(value), v);
}
}
}
}
}
void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
uint16_t packetsOutInt;
packetsOutInt=AQH_SendStatsMsg_GetPacketsOut(nodeMsg);
if (packetsOutInt) {
uint32_t uid;
double packetsOut;
double collisions;
double busy;
double collisionsPercentage=0.0;
double busyPercentage=0.0;
uid=AQH_SendStatsMsg_GetUid(nodeMsg);
packetsOut=/*(double)*/ packetsOutInt;
collisions=/*(double)*/ AQH_SendStatsMsg_GetCollisions(nodeMsg);
busy=/*(double)*/ AQH_SendStatsMsg_GetBusyErrors(nodeMsg);
collisionsPercentage=collisions*100.0/packetsOut;
busyPercentage=busy*100.0/packetsOut;
_publishInt( aqh, uid, "net/packetsOut", 0, NULL, packetsOutInt);
_publishInt( aqh, uid, "net/collisions", 0, NULL, (int) AQH_SendStatsMsg_GetCollisions(nodeMsg));
_publishDouble(aqh, uid, "net/collisionsPercent", 0, "%", collisionsPercentage);
_publishDouble(aqh, uid, "net/busyPercent", 0, "%", busyPercentage);
}
}
void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
uint16_t packetsInInt;
packetsInInt=AQH_RecvStatsMsg_GetPacketsIn(nodeMsg);
if (packetsInInt) {
uint32_t uid;
double packetsIn;
double crcErrors;
double ioErrors;
double crcErrorsPercentage=0.0;
double ioErrorsPercentage=0.0;
uid=AQH_SendStatsMsg_GetUid(nodeMsg);
packetsIn=/*(double)*/ packetsInInt;
crcErrors=/*(double)*/AQH_RecvStatsMsg_GetCrcErrors(nodeMsg);
ioErrors=/*(double)*/AQH_RecvStatsMsg_GetIoErrors(nodeMsg);
crcErrorsPercentage=crcErrors*100.0/packetsIn;
ioErrorsPercentage=ioErrors*100.0/packetsIn;
_publishInt( aqh, uid, "net/packetsIn", 0, NULL, packetsInInt);
_publishInt( aqh, uid, "net/crcerrors", 0, NULL, (int) AQH_RecvStatsMsg_GetCrcErrors(nodeMsg));
_publishInt( aqh, uid, "net/ioerrors", 0, NULL, (int) AQH_RecvStatsMsg_GetIoErrors(nodeMsg));
_publishDouble(aqh, uid, "net/crcerrorsPercent", 0, "%", crcErrorsPercentage);
_publishDouble(aqh, uid, "net/ioerrorsPercent", 0, "%", ioErrorsPercentage);
}
}
void _publishInt(AQHOMED *aqh, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v)
{
_publishDouble(aqh, uid, vPath, vModality, vUnits, /*(double)*/ v);
}
void _publishDouble(AQHOMED *aqh, uint32_t uid, const char *vPath, int vModality, const char *vUnits, double v)
{
GWEN_MSG *pubMsg;
union {double f; uint64_t i;} u;
uint64_t arrayToSend[2];
AQH_VALUE *value;
u.f=v;
arrayToSend[0]=(uint64_t) time(NULL);
arrayToSend[1]=u.i;
value=AQH_Value_new();
_setDeviceName(value, uid);
AQH_Value_SetName(value, vPath);
AQH_Value_SetValueUnits(value, vUnits);
AQH_Value_SetValueType(value, AQH_ValueType_Sensor);
AQH_Value_SetModality(value, vModality);
pubMsg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
GWEN_MsgEndpoint_GetNextMessageId(aqh->brokerEndpoint), 0,
value, arrayToSend, 1);
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN, "BROKER PUBLISH %s: %f", AQH_Value_GetName(value), v);
GWEN_MsgEndpoint_AddSendMessage(aqh->brokerEndpoint, pubMsg);
}
AQH_Value_free(value);
}
void _setDeviceName(AQH_VALUE *value, uint32_t uid)
{
GWEN_BUFFER *buf;
buf=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Buffer_AppendArgs(buf, "%08x", uid);
AQH_Value_SetDeviceName(value, GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_TTY_BROKER_H
#define AQHOMED_LOOP_TTY_BROKER_H
#include "./aqhomed.h"
void AqHomed_ForwardTtyMsgToBroker(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
#endif

View File

@@ -0,0 +1,120 @@
/****************************************************************************
* 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 "./loop_tty_ipc.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./db.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/mqtt/endpoint_mqttc.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _forwardValue2MsgToIpc(GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *nodeMsg);
static void _forwardAnyMsgToIpc(GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *nodeMsg);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ForwardTtyMsgToIpcClients(AQHOMED *aqh, const GWEN_MSG *msg)
{
uint32_t msgGroup;
msgGroup=AQH_NodeMsg_GetMsgGroup(AQH_NodeMsg_GetMsgType(msg));
if (msgGroup) {
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
if (msgGroup & AQH_IpcEndpoint_GetAcceptedMsgGroups(ep)) {
DBG_INFO(NULL, "Endpoint accepts msg group %d", msgGroup);
switch(AQH_NodeMsg_GetMsgType(msg)) {
case AQH_MSG_TYPE_VALUE2:
_forwardValue2MsgToIpc(ep, msg);
break;
default:
_forwardAnyMsgToIpc(ep, msg);
break;
}
}
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
}
else {
DBG_ERROR(NULL, "Message type %d not in any message group, ignoring message", AQH_NodeMsg_GetMsgType(msg));
}
}
void _forwardValue2MsgToIpc(GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *nodeMsg)
{
GWEN_MSG *ipcMsg;
ipcMsg=AQH_ValueIpcMsg_new(AQH_MSGTYPE_IPC_NODES_VALUE,
GWEN_MsgEndpoint_GetNextMessageId(ep), 0,
AQH_Value2Msg_GetUid(nodeMsg),
AQH_Value2Msg_GetValueId(nodeMsg),
AQH_Value2Msg_GetValueType(nodeMsg),
AQH_Value2Msg_GetValueNom(nodeMsg),
AQH_Value2Msg_GetValueDenom(nodeMsg));
GWEN_MsgEndpoint_AddSendMessage(ep, ipcMsg);
}
void _forwardAnyMsgToIpc(GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *nodeMsg)
{
GWEN_MSG *ipcMsg;
ipcMsg=AQH_ForwardIpcMsg_new(AQH_MSGTYPE_IPC_NODES_FORWARD,
GWEN_MsgEndpoint_GetNextMessageId(ep), 0,
GWEN_Msg_GetConstBuffer(nodeMsg), GWEN_Msg_GetBytesInBuffer(nodeMsg));
GWEN_MsgEndpoint_AddSendMessage(ep, ipcMsg);
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_TTY_IPC_H
#define AQHOMED_LOOP_TTY_IPC_H
#include "./aqhomed.h"
void AqHomed_ForwardTtyMsgToIpcClients(AQHOMED *aqh, const GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,215 @@
/****************************************************************************
* 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 "./loop_tty_mqtt.h"
#include "./aqhomed_p.h"
#include "./tty_log.h"
#include "./tty_write.h"
#include "./db.h"
#include "aqhome/msg/endpoint_tty.h"
#include "aqhome/msg/msg_node.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_sendstats.h"
#include "aqhome/msg/msg_recvstats.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/nodes/msg_ipc_forward.h"
#include "aqhome/ipc/nodes/msg_ipc_value.h"
#include "aqhome/mqtt/endpoint_mqttc.h"
#include "aqhome/mqtt/msg_mqtt_publish.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _processValue2Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
static void _publishDouble(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, double v);
static void _publishInt(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, int v);
static void _publishString(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, const char *v);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_ForwardTtyMsgToMqttServer(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
if (GWEN_MsgEndpoint_GetState(aqh->mqttEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
DBG_DEBUG(AQH_LOGDOMAIN, "Processing output message");
switch(AQH_NodeMsg_GetMsgType(nodeMsg)) {
case AQH_MSG_TYPE_VALUE2:
_processValue2Message(aqh, nodeMsg);
break;
case AQH_MSG_TYPE_COMSENDSTATS:
_processSendStatsMessage(aqh, nodeMsg);
break;
case AQH_MSG_TYPE_COMRECVSTATS:
_processRecvStatsMessage(aqh, nodeMsg);
break;
default:
break;
}
}
}
void _processValue2Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
if (AQH_Value2Msg_GetValueType(nodeMsg)==AQH_MSG_VALUE2_TYPE_DOOR)
_publishString(aqh,
AQH_Value2Msg_GetUid(nodeMsg),
AQH_Value2Msg_GetValueId(nodeMsg),
AQH_Value2Msg_GetValueTypeName(nodeMsg),
AQH_Value2Msg_GetValueAsWindowStateString(nodeMsg));
else
_publishDouble(aqh,
AQH_Value2Msg_GetUid(nodeMsg),
AQH_Value2Msg_GetValueId(nodeMsg),
AQH_Value2Msg_GetValueTypeName(nodeMsg),
AQH_Value2Msg_GetValue(nodeMsg));
}
void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
uint16_t packetsOutInt;
packetsOutInt=AQH_SendStatsMsg_GetPacketsOut(nodeMsg);
if (packetsOutInt) {
double packetsOut;
double collisions;
double busy;
double collisionsPercentage=0.0;
double busyPercentage=0.0;
packetsOut=(double) packetsOutInt;
collisions=(double)AQH_SendStatsMsg_GetCollisions(nodeMsg);
busy=(double)AQH_SendStatsMsg_GetBusyErrors(nodeMsg);
collisionsPercentage=collisions*100.0/packetsOut;
busyPercentage=busy*100.0/packetsOut;
_publishInt(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/packetsOut", packetsOutInt);
_publishInt(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/collisions", (int) AQH_SendStatsMsg_GetCollisions(nodeMsg));
_publishDouble(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/collisionsPercent", collisionsPercentage);
_publishDouble(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/busyPercent", busyPercentage);
}
}
void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
{
uint16_t packetsInInt;
packetsInInt=AQH_RecvStatsMsg_GetPacketsIn(nodeMsg);
if (packetsInInt) {
double packetsIn;
double crcErrors;
double ioErrors;
double crcErrorsPercentage=0.0;
double ioErrorsPercentage=0.0;
packetsIn=(double) packetsInInt;
crcErrors=(double)AQH_RecvStatsMsg_GetCrcErrors(nodeMsg);
ioErrors=(double)AQH_RecvStatsMsg_GetIoErrors(nodeMsg);
crcErrorsPercentage=crcErrors*100.0/packetsIn;
ioErrorsPercentage=ioErrors*100.0/packetsIn;
_publishInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/packetsIn", packetsInInt);
_publishInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/crcerrors", (int) AQH_RecvStatsMsg_GetCrcErrors(nodeMsg));
_publishInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/ioerrors", (int) AQH_RecvStatsMsg_GetIoErrors(nodeMsg));
_publishDouble(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/crcerrorsPercent", crcErrorsPercentage);
_publishDouble(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/ioerrorsPercent", ioErrorsPercentage);
}
}
void _publishDouble(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, double v)
{
char numBuf[16];
snprintf(numBuf, sizeof(numBuf)-1, "%f", v);
numBuf[sizeof(numBuf)-1]=0;
_publishString(aqh, uid, valueId, valuePath, numBuf);
}
void _publishInt(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, int v)
{
char numBuf[16];
snprintf(numBuf, sizeof(numBuf)-1, "%d", v);
numBuf[sizeof(numBuf)-1]=0;
_publishString(aqh, uid, valueId, valuePath, numBuf);
}
void _publishString(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, const char *v)
{
GWEN_BUFFER *bufTopic;
GWEN_MSG *pubMsg;
bufTopic=GWEN_Buffer_new(0, 64, 0, 1);
if (valueId>0)
GWEN_Buffer_AppendArgs(bufTopic, "%s/%08x/%d/%s",
aqh->mqttTopicPrefix,
uid,
valueId,
valuePath);
else
GWEN_Buffer_AppendArgs(bufTopic, "%s/%08x/%s",
aqh->mqttTopicPrefix,
uid,
valuePath);
pubMsg=AQH_PublishMqttMsg_new(0, 0, GWEN_Buffer_GetStart(bufTopic), (const uint8_t*) v, strlen(v));
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN, "MQTT PUBLISH %s: %s", GWEN_Buffer_GetStart(bufTopic), v);
GWEN_MsgEndpoint_AddSendMessage(aqh->mqttEndpoint, pubMsg);
}
GWEN_Buffer_free(bufTopic);
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_LOOP_TTY_MQTT_H
#define AQHOMED_LOOP_TTY_MQTT_H
#include "./aqhomed.h"
void AqHomed_ForwardTtyMsgToMqttServer(AQHOMED *aqh, const GWEN_MSG *msg);
#endif

212
apps/aqhome-nodes/main.c Normal file
View File

@@ -0,0 +1,212 @@
/****************************************************************************
* 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 <aqhome/api.h>
#include <aqhome/aqhome.h>
#include "./aqhomed.h"
#include "./init.h"
#include "./fini.h"
#include "./loop.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/cgui.h>
#include <gwenhywfar/debug.h>
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runService(AQHOMED *aqh);
#ifdef HAVE_SIGNAL_H
static int _setSignalHandlers(void);
static int _setupSigAction(struct sigaction *sa, int sig);
static void _signalHandler(int s);
#endif
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
#ifdef HAVE_SIGNAL_H
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT;
#endif
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
int rv;
AQHOMED *aqh;
GWEN_GUI *gui;
rv=GWEN_Init();
if (rv) {
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
return 2;
}
GWEN_Logger_Open(0, "aqhomed", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
rv=_setSignalHandlers();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
rv=AQH_Init();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
gui=GWEN_Gui_CGui_new();
GWEN_Gui_SetGui(gui);
aqh=AqHomed_new();
rv=AqHomed_Init(aqh, argc, argv);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh);
AqHomed_Fini(aqh);
AqHomed_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQHOMED *aqh)
{
time_t timeStart;
int timeout;
timeout=AqHomed_GetTimeout(aqh);
timeStart=time(NULL);
while(!stopService) {
AqHomed_Loop(aqh, 2000);
if (timeout) {
time_t now;
now=time(NULL);
if (timeout && ((int)difftime(now, timeStart))>timeout) {
DBG_INFO(NULL, "Timeout");
break;
}
}
} /* while */
}
int _setSignalHandlers(void)
{
#ifdef HAVE_SIGNAL_H
int rv;
rv=_setupSigAction(&saINT, SIGINT);
if (rv)
return rv;
rv=_setupSigAction(&saTERM, SIGTERM);
if (rv)
return rv;
rv=_setupSigAction(&saHUP, SIGHUP);
if (rv)
return rv;
# ifdef SIGTSTP
rv=_setupSigAction(&saTSTP, SIGTSTP);
if (rv)
return rv;
# endif
# ifdef SIGCONT
rv=_setupSigAction(&saCONT, SIGCONT);
if (rv)
return rv;
# endif
#endif
return 0;
}
int _setupSigAction(struct sigaction *sa, int sig)
{
sa->sa_handler=_signalHandler;
sigemptyset(&sa->sa_mask);
sa->sa_flags=0;
if (sigaction(sig, sa, 0)) {
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
return GWEN_ERROR_IO;
}
return 0;
}
void _signalHandler(int s)
{
switch(s) {
case SIGINT:
case SIGTERM:
case SIGHUP:
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
stopService=1;
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}

View File

@@ -0,0 +1,344 @@
/****************************************************************************
* 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 "./r_setdata.h"
#include "./aqhomed_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/msg_data_set.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/msg/msg_value3.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* definitions
* ------------------------------------------------------------------------------------------------
*/
#define R_SETDATA_REQUEST_EXPIRE_SECS 20
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_NODE_INFO *_getNodeInfoFromValue(AQHOMED *aqh, const AQH_VALUE *value);
static GWEN_MSG_REQUEST *_mkRequest_SetData(AQHOMED *aqh,
GWEN_MSG_ENDPOINT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason);
static void _rqAbort(GWEN_MSG_REQUEST *rq, int reason);
static GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOMED *aqh, int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg);
static void _subRqAbort(GWEN_MSG_REQUEST *rq, int reason);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeNodes_HandleSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
uint32_t msgId;
DBG_INFO(NULL, "Received IPC SetDataRequest message");
msgId=GWEN_IpcMsg_GetMsgId(recvdMsg);
if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
AQH_VALUE *value;
AQH_SetDataIpcMsg_Parse(recvdMsg, 0);
value=AQH_SetDataIpcMsg_ReadValue(recvdMsg);
if (value) {
const char *varName;
varName=AQH_Value_GetName(value);
if (varName) {
char *data;
data=AQH_SetDataIpcMsg_ReadData(recvdMsg);
if (data) {
AQH_NODE_INFO *nodeInfo;
nodeInfo=_getNodeInfoFromValue(aqh, value);
if (nodeInfo) {
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AqHomed_GetDeviceDefByName(aqh, devName);
if (devInfo) {
const AQHNODE_VALUE *devValue;
devValue=AQHNODE_Value_List_GetByName(AQHNODE_Device_GetValueList(devInfo), varName);
if (devValue) {
uint16_t dataVal=0;
uint16_t dataDenom=0;
if (AQH_ReadDataFromString(AQHNODE_Value_GetDataType(devValue), data, &dataVal, &dataDenom)==0) {
GWEN_MSG_REQUEST *rq;
int destAddr;
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
DBG_DEBUG(NULL, "Creating SETDATA request");
rq=_mkRequest_SetData(aqh, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
AqHomed_AddRequestToTree(aqh, rq);
/* done */
}
else {
DBG_ERROR(NULL, "Bad data \"%s\"", data);
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_BADDATA);
}
}
else {
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_TRYAGAIN);
}
}
else {
DBG_ERROR(NULL, "No matching nodeinfo");
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_INVALID);
}
free(data);
}
else {
DBG_ERROR(NULL, "No data");
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_NODATA);
}
}
else {
DBG_ERROR(NULL, "No var name");
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_NODATA);
}
AQH_Value_free(value);
}
else {
DBG_ERROR(NULL, "Could not read value from message");
}
}
else {
DBG_ERROR(NULL, "TTY endpoint not connected");
AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_IO);
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
GWEN_MSG_REQUEST *_mkRequest_SetData(AQHOMED *aqh,
GWEN_MSG_ENDPOINT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
GWEN_MSG_REQUEST *rq;
GWEN_MSG_REQUEST *subRq;
rq=GWEN_MsgRequest_new();
GWEN_MsgRequest_SetPrivateData(rq, aqh);
GWEN_MsgRequest_SetEndpoint(rq, ep);
GWEN_MsgRequest_SetRequestMsgId(rq, requestMsgId);
GWEN_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
GWEN_MsgRequest_SetAbortFn(rq, _rqAbort);
GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
subRq=_mkSubRequest_SetData(aqh, destAddr, valueId, dataVal, dataDenom);
GWEN_MsgRequest_Tree2_AddChild(rq, subRq);
return rq;
}
void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason)
{
GWEN_MSG_ENDPOINT *ep;
uint32_t refMsgId;
int result;
DBG_INFO(NULL, "SubRequest finished (reason: %d)", reason);
refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq);
ep=GWEN_MsgRequest_GetEndpoint(rq);
result=GWEN_MsgRequest_GetResult(subRq);
if (reason==GWEN_MSG_REQUEST_REASON_ABORTED)
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
else
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, result);
GWEN_MsgRequest_SetResult(rq, result);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
}
void _rqAbort(GWEN_MSG_REQUEST *rq, int reason)
{
GWEN_MSG_ENDPOINT *ep;
uint32_t refMsgId;
GWEN_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq);
ep=GWEN_MsgRequest_GetEndpoint(rq);
AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
/* ------------------------------------------------------------------------------------------------
* TTY Request SETDATA
*/
GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOMED *aqh, int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
GWEN_MSG_REQUEST *rq;
uint16_t msgId;
GWEN_MSG *msgOut;
rq=GWEN_MsgRequest_new();
GWEN_MsgRequest_SetPrivateData(rq, aqh);
GWEN_MsgRequest_SetEndpoint(rq, aqh->ttyEndpoint);
GWEN_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
GWEN_MsgRequest_SetAbortFn(rq, _subRqAbort);
msgId=GWEN_MsgEndpoint_GetNextMessageId(aqh->ttyEndpoint) & 0xffff;
GWEN_MsgRequest_SetRequestMsgId(rq, msgId);
GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
msgOut=AQH_Value3Msg_new(aqh->nodeAddress, destAddr, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom);
GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut);
return rq;
}
int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg)
{
AQHOMED *aqh;
uint8_t destAddr;
DBG_DEBUG(NULL, "Checking message from %02x", AQH_NodeMsg_GetSourceAddress(msg));
aqh=(AQHOMED*)GWEN_MsgRequest_GetPrivateData(rq);
destAddr=AQH_NodeMsg_GetDestAddress(msg);
if (destAddr==0xff || destAddr==aqh->nodeAddress) {
uint8_t msgCode;
msgCode=AQH_NodeMsg_GetMsgType(msg);
if (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK || msgCode==AQH_MSG_TYPE_VALUE_SET_NACK) {
uint16_t msgId;
msgId=AQH_Value3Msg_GetMsgId(msg);
if (msgId==GWEN_MsgRequest_GetRequestMsgId(rq)) {
GWEN_MSG_REQUEST *rqParent;
DBG_INFO(NULL,
"Received response (%02x) for msg id %04x from %02x",
msgCode, msgId, AQH_NodeMsg_GetSourceAddress(msg));
GWEN_MsgRequest_SetResult(rq, (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK)?AQH_MSG_IPC_SUCCESS:AQH_MSG_IPC_ERROR_GENERIC);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, GWEN_MSG_REQUEST_REASON_DONE);
return GWEN_MSG_REQUEST_RESULT_HANDLED;
}
else {
DBG_INFO(NULL, " Non-matching message id");
}
}
else {
DBG_INFO(NULL, " Non-matching message code");
}
}
return GWEN_MSG_REQUEST_RESULT_NOT_HANDLED;
}
void _subRqAbort(GWEN_MSG_REQUEST *rq, int reason)
{
GWEN_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
GWEN_MsgRequest_SetResult(rq, AQH_MSG_IPC_ERROR_GENERIC);
GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE);
rqParent=GWEN_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
GWEN_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
AQH_NODE_INFO *_getNodeInfoFromValue(AQHOMED *aqh, const AQH_VALUE *value)
{
const char *s;
unsigned long int uid;
s=AQH_Value_GetDeviceName(value);
if (s && *s && 1==sscanf(s, "%lx", &uid)) {
AQH_NODE_INFO *ni;
ni=AQH_NodeDb_GetNodeInfoByUid(aqh->nodeDb, uid);
if (ni==NULL) {
DBG_ERROR(NULL, "Node \"%08lx\" not found", uid);
return NULL;
}
return ni;
}
return NULL;
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* 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 AQHOMED_R_SETDATA_H
#define AQHOMED_R_SETDATA_H
#include "./aqhomed.h"
#include <gwenhywfar/request.h>
void AqHomeNodes_HandleSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

136
apps/aqhome-nodes/tty_log.c Normal file
View File

@@ -0,0 +1,136 @@
/****************************************************************************
* 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 "./tty_log.h"
#include "./aqhomed_p.h"
#include "aqhome/msg/msg_value.h"
#include "aqhome/msg/msg_value2.h"
#include "aqhome/msg/msg_value3.h"
#include "aqhome/msg/msg_sendstats.h"
#include "aqhome/msg/msg_recvstats.h"
#include "aqhome/msg/msg_memstats.h"
#include "aqhome/msg/msg_sysstats.h"
#include "aqhome/msg/msg_ping.h"
#include "aqhome/msg/msg_pong.h"
#include "aqhome/msg/msg_needaddr.h"
#include "aqhome/msg/msg_claimaddr.h"
#include "aqhome/msg/msg_haveaddr.h"
#include "aqhome/msg/msg_denyaddr.h"
#include "aqhome/msg/msg_device.h"
#include "aqhome/msg/msg_flashready.h"
#include "aqhome/msg/msg_flashstart.h"
#include "aqhome/msg/msg_flashresponse.h"
#include "aqhome/msg/msg_flashend.h"
#include "aqhome/msg/msg_flashdata.h"
#include "aqhome/msg/msg_reboot.h"
#include <gwenhywfar/list.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/gwentime.h>
#include <gwenhywfar/text.h>
#include <stdio.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _writeToLogFile(const char *filename, const char *txt);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
{
if (aqh && aqh->logFile) {
uint8_t msgType;
int msgIsValid;
GWEN_BUFFER *dbuf;
GWEN_TIME *ti;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
ti=GWEN_CurrentTime();
GWEN_Time_toString(ti, "YYYY-MM-DD hh:mm:ss ", dbuf);
GWEN_Time_free(ti);
ti=NULL;
msgIsValid=(AQH_NodeMsg_IsChecksumValid(msg) && AQH_NodeMsg_IsMsgComplete(msg));
msgType=AQH_NodeMsg_GetMsgType(msg);
if (msgIsValid) {
switch(msgType) {
case AQH_MSG_TYPE_PING: AQH_PingMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_PONG: AQH_PongMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_TWIBUSMEMBER: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_DEBUG: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_VALUE: AQH_ValueMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_VALUE2: AQH_Value2Msg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_NEED_ADDRESS: AQH_NeedAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_ClaimAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_HaveAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_DENY_ADDRESS: AQH_DenyAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_DEVICE: AQH_DeviceMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_MEMSTATS: AQH_MemStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_SYSSTATS: AQH_SysStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_FLASH_READY: AQH_FlashReadyMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_FLASH_RSP: AQH_FlashResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_FLASH_DATA: AQH_FlashDataMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_REBOOT_REQ: AQH_RebootRequestMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_REBOOT_RSP: AQH_RebootResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_VALUE_REPORT:
case AQH_MSG_TYPE_VALUE_SET:
case AQH_MSG_TYPE_VALUE_SET_ACK:
case AQH_MSG_TYPE_VALUE_SET_NACK: AQH_Value3Msg_DumpToBuffer(msg, dbuf, "received"); break;
default: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
}
}
else {
AQH_NodeMsg_DumpToBuffer(msg, dbuf, "(invalid) received");
}
_writeToLogFile(aqh->logFile, GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
}
void _writeToLogFile(const char *filename, const char *txt)
{
if (txt && *txt) {
FILE *f;
f=fopen(filename, "a+");
if (f) {
if (1!=fwrite(txt, strlen(txt), 1, f)) {
DBG_ERROR(AQH_LOGDOMAIN, "Error logging.");
}
fclose(f);
}
}
}

View File

@@ -0,0 +1,23 @@
/****************************************************************************
* 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.
****************************************************************************/
#ifndef AQHOMED_TTY_LOG_H
#define AQHOMED_TTY_LOG_H
#include "./aqhomed.h"
void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
#endif

View File

@@ -0,0 +1,73 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhnodes_types" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
device.t2d
value.t2d
</setVar>
<setVar name="local/built_sources" >
device.c
value.c
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
device.h
device_p.h
value.h
value_p.h
</setVar>
<headers dist="true" >
</headers>
<sources>
$(local/typefiles)
</sources>
<useTargets>
</useTargets>
<libraries>
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,87 @@
<?xml?>
<tm2>
<type id="AQHNODE_DEVICE" type="pointer">
<descr>
This object and its objects are used to store registered devices and definitions for possible new devices.
</descr>
<lang id="c">
<identifier>AQHNODE_DEVICE</identifier>
<prefix>AQHNODE_Device</prefix>
<baseFileName>device</baseFileName>
<flags>
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">aqhome-nodes/types/value.h</header>
</headers>
<inlines>
</inlines>
</lang>
<enums>
</enums>
<members>
<member name="name" type="char_ptr" maxlen="128">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="driver" type="char_ptr" maxlen="64">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="manufacturer" type="uint32_t" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="deviceType" type="uint16_t" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="deviceVersion" type="uint16_t" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="valueList" type="AQHNODE_VALUE_LIST" >
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<getflags>none</getflags>
<setflags>none</setflags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,94 @@
<?xml?>
<tm2>
<type id="AQHNODE_VALUE" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQHNODE_VALUE</identifier>
<prefix>AQHNODE_Value</prefix>
<baseFileName>value</baseFileName>
<flags>
with_xml
with_db
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
</headers>
<inlines>
</inlines>
</lang>
<members>
<member name="id" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember</flags>
</member>
<member name="name" type="char_ptr" maxlen="32">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="description" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="valueType" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="dataType" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="modality" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="valueUnits" type="char_ptr" maxlen="32">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="denom" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
</members>
</type>
</tm2>

88
apps/aqhome-react/0BUILD Normal file
View File

@@ -0,0 +1,88 @@
<?xml?>
<gwbuild>
<target type="Program" name="aqhome-react" install="$(sbindir)" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="true" >
aqhome_react.h
aqhome_react_p.h
init.h
fini.h
loop.h
net_read.h
suntimes.h
</headers>
<sources>
$(local/typefiles)
aqhome_react.c
init.c
fini.c
loop.c
net_read.c
suntimes.c
main.c
</sources>
<useTargets>
aqhome
aqhreact_types
aqhreact_units
</useTargets>
<libraries>
$(gwenhywfar_libs)
-lm
</libraries>
<subdirs>
types
units
networks
examples
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,385 @@
/****************************************************************************
* 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 "./aqhome_react_p.h"
#include "./net_read.h"
#include "aqhome-react/units/u_logical.h"
#include "aqhome-react/units/u_valuefilter.h"
#include "aqhome-react/units/u_valueset.h"
#include "aqhome-react/units/u_varset.h"
#include "aqhome-react/units/u_stabilize.h"
#include "aqhome-react/units/u_lowpass.h"
#include "aqhome-react/units/u_highpass.h"
#include "aqhome-react/units/u_zeroposnegstring.h"
#include "aqhome-react/units/u_suntime.h"
#include "aqhome-react/units/u_varchanges.h"
#include "aqhome-react/units/u_timeprogram.h"
#include "aqhome-react/units/u_statfns.h"
#include "aqhome/data/vars_dbwrite.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
AQHOME_REACT *AqHomeReact_new()
{
AQHOME_REACT *aqh;
GWEN_NEW_OBJECT(AQHOME_REACT, aqh);
aqh->unitList=AQHREACT_Unit_List_new();
return aqh;
}
void AqHomeReact_free(AQHOME_REACT *aqh)
{
if (aqh) {
AQHREACT_Unit_List_free(aqh->unitList);
GWEN_MsgEndpoint_free(aqh->brokerEndpoint);
GWEN_DB_Group_free(aqh->dbArgs);
free(aqh->varsFile);
free(aqh->pidFile);
GWEN_FREE_OBJECT(aqh);
}
}
GWEN_MSG_ENDPOINT *AqHomeReact_GetBrokerEndpoint(const AQHOME_REACT *aqh)
{
return aqh?(aqh->brokerEndpoint):NULL;
}
GWEN_DB_NODE *AqHomeReact_GetDbArgs(const AQHOME_REACT *aqh)
{
return aqh?(aqh->dbArgs):NULL;
}
const char *AqHomeReact_GetPidFile(const AQHOME_REACT *aqh)
{
return aqh?aqh->pidFile:NULL;
}
void AqHomeReact_SetPidFile(AQHOME_REACT *aqh, const char *s)
{
if (aqh) {
free(aqh->pidFile);
aqh->pidFile=s?strdup(s):NULL;
}
}
const char *AqHomeReact_GetVarsFile(const AQHOME_REACT *aqh)
{
return aqh?aqh->varsFile:NULL;
}
void AqHomeReact_SetVarsFile(AQHOME_REACT *aqh, const char *s)
{
if (aqh) {
free(aqh->varsFile);
aqh->varsFile=s?strdup(s):NULL;
}
}
int AqHomeReact_GetTimeout(const AQHOME_REACT *aqh)
{
return aqh?aqh->timeout:0;
}
time_t AqHomeReact_GetLatestNetworkFileTime(const AQHOME_REACT *aqh)
{
return aqh?aqh->latestNetworkFileTime:0;
}
void AqHomeReact_SetLatestNetworkFileTime(AQHOME_REACT *aqh, time_t t)
{
if (aqh)
aqh->latestNetworkFileTime=t;
}
AQHREACT_UNIT *AqHomeReact_GetTimerUnit(const AQHOME_REACT *aqh)
{
return aqh?aqh->timerUnit:NULL;
}
AQHREACT_UNIT *AqHomeReact_GetServerVarChangeUnit(const AQHOME_REACT *aqh)
{
return aqh?aqh->serverVarChangeUnit:NULL;
}
AQHREACT_UNIT *AqHomeReact_FindUnitByUnitId(const AQHOME_REACT *aqh, const char *unitId)
{
if (aqh && unitId && *unitId) {
AQHREACT_UNIT *unit;
unit=AQHREACT_Unit_List_GetById(aqh->unitList, unitId);
if (unit==NULL) {
DBG_ERROR(NULL, "Unit \"%s\" not found", unitId);
return NULL;
}
return unit;
}
return NULL;
}
void AqHomeReact_AddUnit(AQHOME_REACT *aqh, AQHREACT_UNIT *unit)
{
if (aqh && unit)
AQHREACT_Unit_List_Add(unit, aqh->unitList);
}
AQH_VARS *AqHomeReact_GetLocalVars(const AQHOME_REACT *aqh)
{
return aqh?aqh->localVars:NULL;
}
int AqHomeReact_SetCharValue(AQHOME_REACT *aqh, const char *path, const char *value)
{
if (aqh && aqh->localVars && path && *path) {
int rv;
uint64_t timestamp;
timestamp=(uint64_t) time(NULL);
rv=AQH_Vars_SetCharValue(aqh->localVars, AQH_VARS_PATHFLAGS_OVERWRITE_VARS, path, value);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
AqHomeReact_UnitVarChanges_StringVarUpdated(aqh->localVarChangeUnit, path, timestamp, value);
return 0;
}
return GWEN_ERROR_INVALID;
}
const char *AqHomeReact_GetCharValue(AQHOME_REACT *aqh, const char *path, int idx, const char *defaultValue)
{
if (aqh && aqh->localVars && path && *path) {
return AQH_Vars_GetCharValue(aqh->localVars, path, idx, defaultValue);
}
return defaultValue;
}
int AqHomeReact_SetDoubleValue(AQHOME_REACT *aqh, const char *path, double value)
{
if (aqh && aqh->localVars && path && *path) {
int rv;
uint64_t timestamp;
timestamp=(uint64_t) time(NULL);
rv=AQH_Vars_SetDoubleValue(aqh->localVars, AQH_VARS_PATHFLAGS_OVERWRITE_VARS, path, value);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
AqHomeReact_UnitVarChanges_DoubleVarUpdated(aqh->localVarChangeUnit, path, timestamp, value);
return 0;
}
return GWEN_ERROR_INVALID;
}
double AqHomeReact_GetDoubleValue(AQHOME_REACT *aqh, const char *path, int idx, double defaultValue)
{
if (aqh && aqh->localVars && path && *path) {
return AQH_Vars_GetDoubleValue(aqh->localVars, path, idx, defaultValue);
}
return defaultValue;
}
int AqHomeReact_SetIntValue(AQHOME_REACT *aqh, const char *path, int value)
{
if (aqh && aqh->localVars && path && *path) {
int rv;
uint64_t timestamp;
timestamp=(uint64_t) time(NULL);
rv=AQH_Vars_SetIntValue(aqh->localVars, AQH_VARS_PATHFLAGS_OVERWRITE_VARS, path, value);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
AqHomeReact_UnitVarChanges_IntVarUpdated(aqh->localVarChangeUnit, path, timestamp, value);
return 0;
}
return GWEN_ERROR_INVALID;
}
int AqHomeReact_GetIntValue(AQHOME_REACT *aqh, const char *path, int idx, int defaultValue)
{
if (aqh && aqh->localVars && path && *path) {
return AQH_Vars_GetIntValue(aqh->localVars, path, idx, defaultValue);
}
return defaultValue;
}
int AqHomeReact_IncIntValue(AQHOME_REACT *aqh, const char *path, int startValue, int defaultValue)
{
if (aqh && aqh->localVars && path && *path) {
int v;
v=AQH_Vars_GetIntValue(aqh->localVars, path, 0, startValue);
v++;
AqHomeReact_SetIntValue(aqh, path, v);
return v;
}
return defaultValue;
}
int AqHomeReact_DecIntValue(AQHOME_REACT *aqh, const char *path, int startValue, int defaultValue)
{
if (aqh && aqh->localVars && path && *path) {
int v;
v=AQH_Vars_GetIntValue(aqh->localVars, path, 0, startValue);
v--;
AqHomeReact_SetIntValue(aqh, path, v);
return v;
}
return defaultValue;
}
int AqHomeReact_WriteVarsFile(AQHOME_REACT *aqh)
{
if (aqh && aqh->localVars && aqh->varsFile) {
int rv;
rv=AQH_Vars_WriteDbFile(aqh->localVars, aqh->varsFile);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
}
return 0;
}
AQHREACT_UNIT *AqHomeReact_CreateUnitByName(AQHOME_REACT *aqh, const char *unitType)
{
/* this does not include u_timer and u_varchanges, because those are only created once globally in init.c */
if (aqh && unitType && *unitType) {
if (strcasecmp(unitType, "or")==0)
return AqHomeReact_UnitOr_new(aqh);
else if (strcasecmp(unitType, "and")==0)
return AqHomeReact_UnitAnd_new(aqh);
else if (strcasecmp(unitType, "xor")==0)
return AqHomeReact_UnitXor_new(aqh);
else if (strcasecmp(unitType, "valueFilter")==0)
return AqHomeReact_UnitValueFilter_new(aqh);
else if (strcasecmp(unitType, "valueSet")==0)
return AqHomeReact_UnitValueSet_new(aqh);
else if (strcasecmp(unitType, "varSet")==0)
return AqHomeReact_UnitVarSet_new(aqh);
else if (strcasecmp(unitType, "stabilize")==0)
return AqHomeReact_UnitStabilize_new(aqh);
else if (strcasecmp(unitType, "lowPass")==0)
return AqHomeReact_UnitLowPass_new(aqh);
else if (strcasecmp(unitType, "highPass")==0)
return AqHomeReact_UnitHighPass_new(aqh);
else if (strcasecmp(unitType, "zeroPosNegString")==0)
return AqHomeReact_UnitZeroPosNegString_new(aqh);
else if (strcasecmp(unitType, "suntime")==0)
return AqHomeReact_UnitSuntime_new(aqh);
else if (strcasecmp(unitType, "timeraction")==0)
return AqHomeReact_UnitTimeProgram_new(aqh);
else if (strcasecmp(unitType, "average")==0)
return AqHomeReact_UnitAverage_new(aqh);
else if (strcasecmp(unitType, "minvalue")==0)
return AqHomeReact_UnitMinValue_new(aqh);
else if (strcasecmp(unitType, "maxvalue")==0)
return AqHomeReact_UnitMaxValue_new(aqh);
else {
AQHREACT_UNIT *unit;
DBG_INFO(NULL, "Trying to load network \"%s\"", unitType);
unit=AQHomeReact_FindAndReadDataDirNetwork(aqh, unitType);
if (unit==NULL) {
DBG_ERROR(NULL, "Unknown unit type \"%s\"", unitType);
return NULL;
}
else {
const char *s;
s=AQHREACT_Unit_GetTypeName(unit);
if (!(s && *s && strcasecmp(s, unitType)==0)) {
DBG_ERROR(NULL, "ERROR: Network file for type \"%s\" contains type \"%s\" instead", unitType, s?s:"<no name>");
AQHREACT_Unit_free(unit);
return NULL;
}
}
return unit;
}
}
return NULL;
}

Some files were not shown because too many files have changed in this diff Show More