560 Commits

Author SHA1 Message Date
Martin Preuss
6174233731 avr: re-added firmware for older devices. 2025-03-11 23:51:14 +01:00
Martin Preuss
216a13e90d aqhome: decreased verbosity. 2025-03-11 23:00:53 +01:00
Martin Preuss
44c5eb65bd aqhome: added AQH_Message_dup() 2025-03-11 23:00:41 +01:00
Martin Preuss
edb6c90f46 decreased verbosity. 2025-03-11 23:00:14 +01:00
Martin Preuss
58bc39c2fb aqhome-tool: Decreased verbosity, fixed flash handling. 2025-03-11 23:00:02 +01:00
Martin Preuss
fecaaaf341 aqhome apps: fixed taglist handling. 2025-03-11 22:59:18 +01:00
Martin Preuss
263ce00a14 more work on new events2-based apps. 2025-03-11 00:56:18 +01:00
Martin Preuss
9cca3af402 aqhome apps: sending a message via aqhome-nodes to nodes now works. 2025-03-10 23:22:30 +01:00
Martin Preuss
541b5ee2ca added file to .gitignore 2025-03-10 00:15:52 +01:00
Martin Preuss
dc4a02a8ff aqhome-apps: removed unneeded files. 2025-03-10 00:15:36 +01:00
Martin Preuss
5011e7e123 aqhome-apps: decreased verbosity. 2025-03-10 00:02:26 +01:00
Martin Preuss
cf9408b594 avr: no longer build firmware for outdated nodes. 2025-03-09 23:25:27 +01:00
Martin Preuss
3e4e3ffe2d aqhome-apps: all apps now work again. 2025-03-09 23:25:02 +01:00
Martin Preuss
9c1188b4d1 aqhome: decreased verbosity. 2025-03-09 15:04:03 +01:00
Martin Preuss
ea564ba101 aqhome apps: more work on transition to events2. 2025-03-09 00:06:12 +01:00
Martin Preuss
ca2103f7b3 aqhome: adapted server aqhome-mqttlog to events2 api. 2025-03-08 01:03:22 +01:00
Martin Preuss
58c6d12e36 aqhome: finished transformation of aqhome-data and aqhome-tool. 2025-03-02 21:48:22 +01:00
Martin Preuss
2f468e4f78 added device descriptor for t03. 2025-03-01 16:58:18 +01:00
Martin Preuss
daba490c09 events2: make sure object is disconnected from event loop! 2025-03-01 16:58:04 +01:00
Martin Preuss
3638378344 ipc2: added missing include. 2025-03-01 16:57:36 +01:00
Martin Preuss
f26effc8b3 aqhome-data: increased verbosity. 2025-03-01 16:57:20 +01:00
Martin Preuss
8b22e7d22a aqhome-nodes: don't directly delete endpoint when connection goes down.
object should only be deleted outside the loop!
2025-03-01 16:57:00 +01:00
Martin Preuss
b413b172e5 ipc2: minor beautification. 2025-03-01 15:23:56 +01:00
Martin Preuss
4e85b59ec9 aqhome: added missing defs. 2025-03-01 15:23:26 +01:00
Martin Preuss
c7551512bc aqhome: added AQH_IpcdMessageValues_newForOne() 2025-03-01 15:23:07 +01:00
Martin Preuss
6b61763d6f ipc2: added ipc_endpoint 2025-03-01 15:22:34 +01:00
Martin Preuss
106f47d465 aqhome-tool: more work on transformation to events2. 2025-03-01 15:22:00 +01:00
Martin Preuss
0cfec70025 aqhome-nodes: transformed app to use new event2 interface. 2025-03-01 15:21:02 +01:00
Martin Preuss
72e32847c7 aqhome-mqttlog: fixed compiler warning. 2025-03-01 15:20:29 +01:00
Martin Preuss
c6f4759530 aqhome-data: removed unneeded files. 2025-03-01 15:19:58 +01:00
Martin Preuss
3e9aa7969b aqhome: add apiCode to endpoint. 2025-02-28 00:22:26 +01:00
Martin Preuss
e308e07b87 aqhome-tool: move basic client code on folder up. 2025-02-27 23:55:29 +01:00
Martin Preuss
f2d527cd2f aqhome-data, aqhome-tool: more work on new protocol. 2025-02-27 23:50:18 +01:00
Martin Preuss
d887747b3c aqhome: more work on transformation to event2/ipc2. 2025-02-27 14:08:44 +01:00
Martin Preuss
bebc4c1b0d avr: started working on hw interface for uart0. 2025-02-26 21:00:09 +01:00
Martin Preuss
8968f14122 aqhome: more work on new event/ipc interface. 2025-02-26 20:59:20 +01:00
Martin Preuss
f63079af11 aqhome: Prepared reorganizing IPC and nodes code around built-in event2 api. 2025-02-26 00:49:33 +01:00
Martin Preuss
cf8edbbd5f aqhome: started rewriting message code, start using new event2 lib. 2025-02-25 01:13:07 +01:00
Martin Preuss
f1f24168e5 t03: send memory stats. 2025-02-14 22:45:49 +01:00
Martin Preuss
d5d6217c5e avr: added NET_Interface_AddOrReleaseOutMsg and NET_Interface_GetNumOfOutgoingMsgNums 2025-02-14 22:43:24 +01:00
Martin Preuss
b60de3994c avr: added routine to count used buffers. fixed a bug. 2025-02-14 22:42:46 +01:00
Martin Preuss
6e25647c0a avr: echoing messages via tty now basically works. 2025-02-13 23:52:55 +01:00
Martin Preuss
a7990db831 avr: t03 can now send and receive messages!
will change other nodes from com2 interface to new network interface.
2025-02-13 18:56:13 +01:00
Martin Preuss
bf61be029e avr: introduced network module
this will be the base module for network modules.
2025-02-13 01:12:29 +01:00
Martin Preuss
c5ab06b6d0 avr: fixed apidoc. 2025-02-13 01:10:32 +01:00
Martin Preuss
bcc7194254 avr: added guarded ringbuffer routines 2025-02-13 01:10:15 +01:00
Martin Preuss
2a776ca895 avr: added UART_HW_IFACE_OFFS_WRITEMSGRINGBUF 2025-02-12 00:37:24 +01:00
Martin Preuss
35f2c2bd7e avr: fixed code order.
only disable interrupts if message sent completely.
2025-02-12 00:36:54 +01:00
Martin Preuss
393d4b4f56 avr: fixed apidoc. 2025-02-12 00:36:21 +01:00
Martin Preuss
4339a8e80b avr: added RINGBUFFERY_SIZE 2025-02-12 00:35:47 +01:00
Martin Preuss
351ab57d62 avr: t03 now at least writes tty message once! 2025-02-11 01:13:00 +01:00
Martin Preuss
0790ac0dea avr: more work on t03 and hw uart modules.
Too complicated, will start new...
2025-02-10 23:36:52 +01:00
Martin Preuss
358ceaaa7d aqhome: add flags to socket to dump incoming data (needs latest gwen). 2025-02-10 23:36:23 +01:00
Martin Preuss
50ba9ee3a1 avr: added missing files. 2025-02-09 21:09:03 +01:00
Martin Preuss
703f8042f9 avr: more work on ardware uart code. 2025-02-09 21:06:31 +01:00
Martin Preuss
601559ca6e avr: added RingBufferY_PeekByte 2025-02-09 21:05:48 +01:00
Martin Preuss
a36639ed8c avr: fixed UID generation (bad code order). 2025-02-09 21:05:24 +01:00
Martin Preuss
224a5f336a avr: added desciption file for n23. 2025-02-09 21:04:56 +01:00
Martin Preuss
9719063a21 avr: adapted flash4p code for single page erasing mcu. 2025-02-09 21:04:30 +01:00
Martin Preuss
dbe23f7e73 avr: added n23. 2025-02-09 21:03:40 +01:00
Martin Preuss
702acb3304 avr: adapted boot firmware for n21 abd n22. 2025-02-09 21:03:28 +01:00
Martin Preuss
cc8dd6e22f aqhome: modified setup of serial port. 2025-02-01 16:22:36 +01:00
Martin Preuss
bb73225b86 avr: improved n20. 2025-02-01 16:22:03 +01:00
Martin Preuss
31ca7ae529 avr: more work on t03. 2025-02-01 16:21:46 +01:00
Martin Preuss
64e781f82f avr: fixed apidoc. 2025-02-01 16:21:25 +01:00
Martin Preuss
2d259ae9be aqhome-tool: increased verbosity for flashing. 2025-02-01 16:20:57 +01:00
Martin Preuss
b38d864612 avr: more work on uart_hw module. 2025-01-29 01:19:07 +01:00
Martin Preuss
52bbfcfb15 avr: more work on hardware based uart module. 2025-01-27 00:20:45 +01:00
Martin Preuss
a96bd7fc07 Revert "uart_hw: added flush/skip routine."
This reverts commit 7962ff6213.
2025-01-25 12:52:41 +01:00
Martin Preuss
7962ff6213 uart_hw: added flush/skip routine. 2025-01-25 12:52:24 +01:00
Martin Preuss
e840bfd9e6 avr: t03 runs in basic mode now, flashing of AtTiny841 finally works!! 2025-01-25 03:16:02 +01:00
Martin Preuss
779b37f195 avr: added flash4p (flashing basically works now). 2025-01-24 21:39:21 +01:00
Martin Preuss
dfbc10149c t03: added debug code. 2025-01-22 01:10:49 +01:00
Martin Preuss
22a5402141 avr: receiving flash messages basically works. 2025-01-22 01:10:32 +01:00
Martin Preuss
19af9daea7 aqhome-tool: temporarily added delay between sending of packages. 2025-01-22 01:09:56 +01:00
Martin Preuss
0a10d136d5 avr: bootloader partially works now but stops after 3 messages... 2025-01-20 23:47:13 +01:00
Martin Preuss
0d7aca0060 t03: use new bootloader code. 2025-01-19 15:49:18 +01:00
Martin Preuss
7d33e451cd .gitignore: added simple coredump to list. 2025-01-19 15:48:59 +01:00
Martin Preuss
d631c5465f avr: minor work on init code. 2025-01-19 15:48:08 +01:00
Martin Preuss
ecb2d85ea2 avr: more work on hardware-based UART module. 2025-01-19 15:46:21 +01:00
Martin Preuss
c390b1059c avr: updated flash procedure code. 2025-01-19 15:44:46 +01:00
Martin Preuss
ba279ae2bb avr: fixed clobbered list. 2025-01-19 15:43:48 +01:00
Martin Preuss
30a2743f19 avr: try to outsource timer tables. 2025-01-16 17:08:15 +01:00
Martin Preuss
46d939fa31 avr: blank char change. 2025-01-16 17:07:50 +01:00
Martin Preuss
f2aff6c235 avr: started working on device t03. 2025-01-16 17:07:27 +01:00
Martin Preuss
e97a5c0720 avr: added missing modules for device n20. 2025-01-16 17:06:54 +01:00
Martin Preuss
6dd9a3ba8f avr: use ringbuffer macros in uart_irq. 2025-01-16 17:06:06 +01:00
Martin Preuss
1775fb7785 avr: more work on uart_hw. 2025-01-16 17:05:42 +01:00
Martin Preuss
a639316cdf avr: added code for fixed buffers. 2025-01-16 17:05:07 +01:00
Martin Preuss
87706e1265 avr: more ringbuffer macros and code. 2025-01-16 17:04:18 +01:00
Martin Preuss
fff64ae41e uart_hw: removed jumptable approach. 2025-01-15 00:13:02 +01:00
Martin Preuss
3633bb03fc avr: started working on hardware-based UART module. 2025-01-15 00:12:06 +01:00
Martin Preuss
172998a5c4 avr: add macros for ringbuffers using Y reg. 2025-01-15 00:11:08 +01:00
Martin Preuss
acbe4505b9 ipc2: started working on event-based ipc handling. 2025-01-09 01:42:19 +01:00
Martin Preuss
b2193afa46 added some files to .gitignore. 2025-01-07 22:44:44 +01:00
Martin Preuss
28b130ecd4 aqhome: started rewriting IPC module.
started with basic event interface using unix fd and timers.
2025-01-07 22:43:20 +01:00
Martin Preuss
2ac1182879 etc: fixed a typo. 2025-01-05 00:54:46 +01:00
Martin Preuss
f2d18fceee add device descriptions for n21 and n22. 2025-01-05 00:54:28 +01:00
Martin Preuss
43c23d754c n21: use module TCRT1000 (door sensor). 2025-01-05 00:54:08 +01:00
Martin Preuss
8eee81c682 systemd services: use /var/lib instead of /var/cache for permanent data. 2025-01-05 00:53:04 +01:00
Martin Preuss
34d395bb0b aqhome-mqttlog: read client id from config file if not given as argument. 2025-01-05 00:51:13 +01:00
Martin Preuss
277af02f68 mqtt: added descriptor for old tasmota setup.
Previously I used to rename tasmota plugs to "plugXX". We now use the device
id out of the box.
2025-01-05 00:50:28 +01:00
Martin Preuss
68ef24ea9b si7021: fixed a minor bug. 2025-01-05 00:49:11 +01:00
Martin Preuss
a3d5b33105 mainly built-fixes. 2025-01-01 19:33:32 +01:00
Martin Preuss
a7adf15bf6 avr: call Motion_Run if enabled. 2024-12-17 20:54:07 +01:00
Martin Preuss
a435b995a8 avr: beautifications. 2024-12-17 20:53:18 +01:00
Martin Preuss
9d71ff27a7 avr: cleanup motion module. 2024-12-15 22:54:38 +01:00
Martin Preuss
090e178ed1 tcrt1000: fixed comment. 2024-12-15 22:15:00 +01:00
Martin Preuss
a78e354c28 motion: increased signal stability for motion module. 2024-12-15 22:14:45 +01:00
Martin Preuss
9c87c3e5f3 motion: repeat messages. 2024-12-15 19:56:38 +01:00
Martin Preuss
01133cc35b avr: fixed defs. 2024-12-15 18:21:10 +01:00
Martin Preuss
4dc6031d03 avr: added devices, more work on modules. 2024-12-15 18:20:54 +01:00
Martin Preuss
c3fd458769 avr: renamed some files. 2024-12-15 18:19:09 +01:00
Martin Preuss
433720525d aqhome: handle types for CO2 and TVOC. 2024-12-15 18:17:35 +01:00
Martin Preuss
375ab592ff avr: added ccs811 module (air quality sensor). 2024-12-02 23:57:31 +01:00
Martin Preuss
d2694df67c avr: more work on motion activated light module. 2024-12-02 23:57:06 +01:00
Martin Preuss
2270163837 avr: added module for TCRT1000 (reflex coupler) 2024-12-02 23:55:39 +01:00
Martin Preuss
9282ac3bb2 avr: added defs for motionLight module conf in EEPROM. 2024-11-04 23:29:21 +01:00
Martin Preuss
6f858e3909 avr: use REED_OnPacketReceived and MotionLight_OnPacketReceived. 2024-11-04 23:28:55 +01:00
Martin Preuss
cf26a01bb8 avr: motionLight module now reads/writes its config from/into EEPROM. 2024-11-04 23:28:13 +01:00
Martin Preuss
2e3705946e avr: added SK6812_OnPacketReceived 2024-11-04 23:27:16 +01:00
Martin Preuss
42f76f9d09 avr: added REED_OnPacketReceived 2024-11-04 23:27:00 +01:00
Martin Preuss
bc7a549513 avr: added def and code for CPRO_CMD_DATA 2024-11-03 15:35:32 +01:00
Martin Preuss
f002587aee aqhome: fixed handling of AQH_ValueDataTyp 2024-11-03 15:35:08 +01:00
Martin Preuss
a520b37089 avr: remove code for old LED module. 2024-11-03 15:34:40 +01:00
Martin Preuss
4f628a16c6 avr: added modules to n14 node. 2024-11-03 15:34:21 +01:00
Martin Preuss
630dc2cecb avr: added routine CPRO_ReadValue 2024-11-03 15:33:56 +01:00
Martin Preuss
51a585740d avr: added configurable values for N14 module. 2024-11-03 15:33:37 +01:00
Martin Preuss
dab4980a7a avr: add missing build file. 2024-11-03 15:33:04 +01:00
Martin Preuss
e847130f0c avr: handle ma_light module in main module. 2024-11-03 15:32:46 +01:00
Martin Preuss
4c88c15874 avr: added missing folders. 2024-11-03 15:32:21 +01:00
Martin Preuss
132ec3ce95 avr: added motion_activated_light module. 2024-11-03 15:31:59 +01:00
Martin Preuss
9178a6fca5 avr: indentation changes. 2024-11-03 15:29:16 +01:00
Martin Preuss
eb68e66746 avr: adapted pin defs. 2024-10-31 22:24:42 +01:00
Martin Preuss
b72d474a7f avr: added tests fir uart_irq. 2024-10-31 22:23:58 +01:00
Martin Preuss
2b68bfd3af avr: reduce resend time. 2024-10-31 22:23:28 +01:00
Martin Preuss
e4a0e8557a avr: added routine sk6812SendPattern 2024-10-31 22:23:08 +01:00
Martin Preuss
57bbefdf38 Revert "avr: started working on value manager"
This reverts commit dfad168875.
2024-10-31 18:57:06 +01:00
Martin Preuss
7707cb0a82 avr started working on irq driven uart module.
will be used for routers and usb-serial interface.
2024-10-31 18:50:53 +01:00
Martin Preuss
dfad168875 avr: started working on value manager
will probably not use this since this takes many bytes in flash.
2024-10-31 18:50:08 +01:00
Martin Preuss
7fde61f849 avr: added device n15. 2024-10-28 23:44:58 +01:00
Martin Preuss
8904d33789 avr: implemented motion detector module. 2024-10-28 23:44:34 +01:00
Martin Preuss
d28e20b179 avr: allow for adjustable timing in simple LED module. 2024-10-28 23:44:08 +01:00
Martin Preuss
c5915b5583 avr: more work on bitbang module (works now). 2024-10-28 23:43:19 +01:00
Martin Preuss
2626c4365c avr: add delay when sending messages in boot loader. 2024-10-28 23:42:55 +01:00
Martin Preuss
9c35e7a006 avr: adapted boot firmware to latest changes (mainly switch to uart_bitbang). 2024-10-28 23:42:30 +01:00
Martin Preuss
061438b7c8 avr, aqhome: added modality MOTION. 2024-10-28 23:41:27 +01:00
Martin Preuss
88035efdf9 avr: started working on device n15. 2024-10-25 00:15:45 +02:00
Martin Preuss
3546c93d23 avr: adapted to latest changes in COM2 module (using uartBitbang) 2024-10-25 00:15:24 +02:00
Martin Preuss
e232b4adbf avr: fixed a few errors in new uart_bitbang module. 2024-10-20 23:35:26 +02:00
Martin Preuss
89019f1e60 avr: removed unneeded code. 2024-10-20 23:09:38 +02:00
Martin Preuss
efc91241d9 avr: new uart_bitbang module works now. 2024-10-20 23:09:24 +02:00
Martin Preuss
c90c33ec11 avr: fixed a bug. 2024-10-20 23:08:17 +02:00
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
822 changed files with 87341 additions and 7795 deletions

20
.gitignore vendored
View File

@@ -4,3 +4,23 @@ 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
aqhome-react.pid
core.*
core
aqhome.geany
aqhome-nodes.db
aqhome-nodes.db.*

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/

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

@@ -0,0 +1,88 @@
<?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" >
server.h
server_p.h
s_connect.h
s_getdevices.h
s_getvalues.h
s_addvalue.h
s_annvalue.h
s_updatedata.h
s_setdata.h
s_getdatapoints.h
s_moddevice.h
</headers>
<sources>
$(local/typefiles)
server.c
s_connect.c
s_getdevices.c
s_getvalues.c
s_addvalue.c
s_annvalue.c
s_updatedata.c
s_setdata.c
s_getdatapoints.c
s_moddevice.c
main.c
</sources>
<useTargets>
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

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

@@ -0,0 +1,267 @@
/****************************************************************************
* 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 "./server.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(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
static void _writeCurrentState(AQH_OBJECT *aqh);
static int _diffInSeconds(time_t t1, time_t t0);
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
#ifdef HAVE_SIGNAL_H
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
#endif
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
int rv;
AQH_EVENT_LOOP *eventLoop;
AQH_OBJECT *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);
eventLoop=AQH_EventLoop_new();
aqh=AqHomeDataServer_new(eventLoop);
rv=AqHomeDataServer_Init(aqh, argc, argv);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh, eventLoop);
//AqHomeData_Fini(aqh);
AQH_Object_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
{
time_t timeStart;
time_t timeLastWrite;
time_t timeLastConnectionCleanup;
int timeout;
timeout=AqHomeDataServer_GetTimeout(aqh);
timeStart=time(NULL);
timeLastWrite=time(NULL);
timeLastConnectionCleanup=time(NULL);
while(!stopService) {
time_t now;
DBG_INFO(NULL, "Next loop (%d clients)", AqHomeDataServer_GetClientNum(aqh));
AQH_EventLoop_Run(eventLoop, 2000);
AqHomeDataServer_HandleClientMsgs(aqh);
now=time(NULL);
if (_diffInSeconds(now, timeLastConnectionCleanup)>CONNCLEAN_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Cleanup connections");
AqHomeDataServer_CleanupClients(aqh);
timeLastConnectionCleanup=now;
}
if (_diffInSeconds(now, timeLastWrite)>WRITE_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Write time");
_writeCurrentState(aqh);
timeLastWrite=now;
}
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
DBG_ERROR(NULL, "Timeout");
_writeCurrentState(aqh);
break;
}
} /* while */
}
int _diffInSeconds(time_t t1, time_t t0)
{
return t1-t0;
}
void _writeCurrentState(AQH_OBJECT *aqh)
{
int rv;
rv=AqHomeDataServer_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;
rv=_setupSigAction(&saPIPE, SIGPIPE);
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;
case SIGPIPE:
DBG_WARN(0, "Received PIPE signal");
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}

View File

@@ -0,0 +1,67 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_addvalue.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
AQH_VALUE *recvdValue;
recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList);
if (recvdValue) {
AQH_VALUE *value;
value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
if (value==NULL)
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
AQH_Value_free(recvdValue);
}
else
resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA;
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
AQH_IPC_PROTOCOL_DATA_VERSION,
AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_ADDVALUE_H
#define AQHOME_DATA_S_ADDVALUE_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,50 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_annvalue.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
AQH_VALUE *recvdValue;
recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList);
if (recvdValue) {
AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
AQH_Value_free(recvdValue);
}
}
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_ANNVALUE_H
#define AQHOME_DATA_S_ANNVALUE_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,82 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_connect.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleConnect(GWEN_UNUSED AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
char *clientId=NULL;
char *userId=NULL;
char *passw=NULL;
uint32_t flags;
clientId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_CLIENTID, NULL);
userId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_USERID, NULL);
flags=AQH_Tag16_GetTagDataAsUint32(tagList, AQH_MSG_CONNECT_TAGS_FLAGS, 0);
passw=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_PASSWORD, NULL);
if (clientId)
AQH_Endpoint_SetServiceName(ep, clientId);
if (userId)
AQH_Endpoint_SetUserName(ep, userId);
if (flags & AQH_MSG_CONNECT_FLAGS_WANTUPDATES)
AQH_Endpoint_AddFlags(ep, AQH_ENDPOINT_FLAGS_WANTUPDATES);
/* TODO: add user management, for now we allow all */
AQH_Endpoint_SetPermissions(ep,
AQH_ENDPOINT_PERMS_LISTVALUES |
AQH_ENDPOINT_PERMS_READVALUE |
AQH_ENDPOINT_PERMS_ADDVALUE |
AQH_ENDPOINT_PERMS_LISTDATA |
AQH_ENDPOINT_PERMS_READDATA |
AQH_ENDPOINT_PERMS_ADDDATA |
AQH_ENDPOINT_PERMS_LISTDEVICES |
AQH_ENDPOINT_PERMS_READDEVICE |
AQH_ENDPOINT_PERMS_ADDDEVICE |
AQH_ENDPOINT_PERMS_MODDEVICE);
free(passw);
free(userId);
free(clientId);
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
AQH_IPC_PROTOCOL_DATA_VERSION,
AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_CONNECT_H
#define AQHOME_DATA_S_CONNECT_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleConnect(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,228 @@
/****************************************************************************
* 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 "./s_getdatapoints.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId);
static int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId);
static void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(recvdMsg);
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_READDATA) {
char *valueName;
valueName=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
if (valueName && *valueName) {
AQH_VALUE *value;
uint64_t tsBegin;
uint64_t tsEnd;
uint64_t numRequested;
tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0);
numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
if (value) {
int resultCode;
resultCode=_getAndSendDataPoints(xo->storage, ep, value, tsBegin, tsEnd, numRequested, refMsgId);
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
}
else {
DBG_INFO(NULL, "Value \"%s\" does not exist", valueName);
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
}
free(valueName);
}
else {
DBG_INFO(NULL, "Missing value name");
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_PERMS);
}
}
}
}
int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
if (num==0)
return _getAndSendDataPointsNoNum(storage, ep, value, tsBegin, tsEnd, refMsgId);
else if (num==1) {
_getAndSendLastDatapoint(storage, ep, value, refMsgId);
return AQH_MSGDATA_RESULT_SUCCESS;
}
else
return _getAndSendDataPointsWithNum(storage, ep, value, num, refMsgId);
}
int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *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(storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
if (tablePtr) {
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSGDATA_RESULT_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSGDATA_RESULT_ERROR_NODATA;
}
}
int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *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(storage, valueId, num);
if (tablePtr) {
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSGDATA_RESULT_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSGDATA_RESULT_ERROR_NODATA;
}
}
void _sendDataPointsResponse(AQH_OBJECT *ep,
const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId)
{
int numTableEntries;
int numDataPoints;
AQH_MESSAGE *outMsg;
numTableEntries=(int)(tablePtr[0]);
numDataPoints=numTableEntries/2;
outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
value, &(tablePtr[1]), numDataPoints);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint32_t refMsgId)
{
int rv;
uint64_t timestamp=0;
double data=0.0;
rv=AQH_Storage_GetLastDataPoint(storage, AQH_Value_GetId(value), &timestamp, &data);
if (rv<0) {
int resultCode;
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSGDATA_RESULT_ERROR_NODATA; break;
default: resultCode=AQH_MSGDATA_RESULT_ERROR_GENERIC; break;
}
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
}
else {
AQH_MESSAGE *outMsg;
outMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
value, timestamp, data);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_GETDATAPOINTS_H
#define AQHOME_DATA_S_GETDATAPOINTS_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,118 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_getdevices.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_DEVICESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
const AQH_DEVICE_LIST *origDeviceList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_ERROR(NULL, "HandleGetDevices");
origDeviceList=AQH_Storage_GetDeviceList(xo->storage);
if (origDeviceList) {
DBG_ERROR(NULL, "Have a list of %d devices", AQH_Device_List_GetCount(origDeviceList));
if (AQH_Device_List_GetCount(origDeviceList)<AQHOMEDATA_DEVICESPERMSG) {
DBG_ERROR(NULL, "Sending all entries in one message");
_sendDeviceList(ep, origDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
}
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_ERROR(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
AQH_Device_List_Clear(tmpDeviceList);
}
v=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_ERROR(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
}
else {
/* empty list */
_sendDeviceList(ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
}
}
}
}
void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;
DBG_ERROR(NULL, "Sending msg (refMsgId=%d)", refMsgId);
msg=AQH_IpcdMessageDevices_new(AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP, AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags, vl);
AQH_Endpoint_AddMsgOut(ep, msg);
}

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 AQHOME_DATA_S_GETDEVICES_H
#define AQHOME_DATA_S_GETDEVICES_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,119 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_getvalues.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_VALUESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
const AQH_VALUE_LIST *origValueList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_INFO(NULL, "HandleGetValues");
origValueList=AQH_Storage_GetValueList(xo->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(ep, origValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
}
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(ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
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(ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
}
else {
/* empty list */
_sendValueList(ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
}
}
}
}
void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;
DBG_ERROR(NULL, "Sending msg (refMsgId=%d)", refMsgId);
msg=AQH_IpcdMessageValues_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP, AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags, vl);
AQH_Endpoint_AddMsgOut(ep, msg);
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_GETVALUES_H
#define AQHOME_DATA_S_GETVALUES_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,105 @@
/****************************************************************************
* 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 "./s_moddevice.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_MODDEVICE) {
AQH_DEVICE *device;
device=AQH_IpcdMessageDevices_ReadFirstDevice(tagList);
if (device) {
const char *deviceNameForSystem;
deviceNameForSystem=AQH_Device_GetNameForSystem(device);
if (deviceNameForSystem && *deviceNameForSystem) {
AQH_DEVICE *storedDevice;
storedDevice=AQH_Storage_GetDeviceByNameForSystem(xo->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(xo->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
resultCode=AQH_MSGDATA_RESULT_SUCCESS;
}
else {
DBG_INFO(NULL, "Device \"%s\" not found", deviceNameForSystem);
resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No name for value");
resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No device info in message");
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
}
}
else {
DBG_ERROR(NULL, "No permissions to read data");
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
}
AqHomeDataServer_SendResponseResultToEndpoint(ep, AQH_IpcMessage_GetMsgId(recvdMsg), resultCode);
}
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_MODDEVICE_H
#define AQHOME_DATA_S_MODDEVICE_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,282 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_setdata.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_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 AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, const char *data);
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data);
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
uint32_t msgId;
AQH_VALUE *recvdValue;
msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
DBG_INFO(NULL, "Received IPC SetDataRequest message (msgId=%d)", msgId);
recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
if (recvdValue) {
const char *valueName;
char *valueDataFreeable;
AQH_VALUE *systemValue;
valueName=AQH_Value_GetNameForSystem(recvdValue);
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
systemValue=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
if (systemValue) {
if (AQH_Value_GetValueType(systemValue)==AQH_ValueType_Actor) {
const char *driverName;
driverName=AQH_Value_GetDriver(systemValue);
if (driverName && *driverName) {
AQH_OBJECT *epDriver;
epDriver=AqHomeDataServer_GetIpcEndpointByServiceName(o, driverName);
if (epDriver) {
AQH_MSG_REQUEST *rq;
DBG_ERROR(NULL, "Creating SETDATA request for driver endpoint (%s)", AQH_Endpoint_GetServiceName(epDriver));
rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueDataFreeable);
AqHomeDataServer_AddRequestToTree(o, rq);
}
else {
DBG_ERROR(NULL, "Driver \"%s\" not available", driverName);
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
}
}
else {
DBG_ERROR(NULL, "No driver name");
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
}
} /* if actor */
else {
DBG_ERROR(NULL, "Value \"%s\" is not an actor", valueName);
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Unknown value \"%s\"", valueName);
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
}
AQH_Value_free(recvdValue);
free(valueDataFreeable);
} /* if recvdValue */
else {
DBG_ERROR(NULL, "No value in message");
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, const char *data)
{
AQH_MSG_REQUEST *rq;
AQH_MSG_REQUEST *subRq;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, epSrc);
AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
subRq=_mkSubRequest_SetData(o, epDriver, v, data);
AQH_MsgRequest_Tree2_AddChild(rq, subRq);
return rq;
}
void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
int result;
DBG_DEBUG(NULL, "SubRequest finished (reason: %d)", reason);
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
result=AQH_MsgRequest_GetResult(subRq);
if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
else
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, result);
AQH_MsgRequest_SetResult(rq, result);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
}
void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
/* ------------------------------------------------------------------------------------------------
* Driver Request SETDATA
*/
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data)
{
AQH_MSG_REQUEST *rq;
uint16_t msgId;
AQH_MESSAGE *driverMsg;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, epDriver);
AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);
msgId=AQH_Endpoint_GetNextMessageId(epDriver);
AQH_MsgRequest_SetRequestMsgId(rq, msgId);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
driverMsg=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data);
AQH_Endpoint_AddMsgOut(epDriver, driverMsg);
return rq;
}
int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
{
DBG_DEBUG(NULL, "Checking message from driver");
if (AQH_IpcMessage_GetCode(msg)==AQH_MSGTYPE_IPC_DATA_RESULT) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0);
if (tagList) {
uint32_t result;
AQH_MSG_REQUEST *rqParent;
result=AQH_IpcMessageResult_GetResult(tagList);
DBG_INFO(NULL, "Received result for request: %d", result);
AQH_MsgRequest_SetResult(rq, result);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);
GWEN_Tag16_List_free(tagList);
return AQH_MSG_REQUEST_RESULT_HANDLED;
}
else {
DBG_ERROR(NULL, "Bad message %d (no TAG16 data)", AQH_IpcMessage_GetCode(msg));
}
}
else {
DBG_ERROR(NULL, "Unexpected response message %d", AQH_IpcMessage_GetCode(msg));
}
return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
}
void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_SETDATA_H
#define AQHOME_DATA_S_SETDATA_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,171 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_updatedata.h"
#include "./server_p.h"
#include <aqhome/data/value.h>
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define DISABLE_DEBUGLOG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _storeDataPoints(AQHOME_SERVER *xo, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues);
static void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc,
const AQH_VALUE *v, const uint64_t *dataPoints, int numValues);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
AQH_VALUE *recvdValue;
recvdValue=AQH_IpcdMessageMultiData_ReadValue(tagList);
if (recvdValue) {
const char *valueName;
const uint64_t *dataPoints=NULL;
uint64_t numberOfPoints=0;
valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL;
AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &dataPoints, &numberOfPoints);
if (numberOfPoints>0) {
AQH_VALUE *value;
value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue);
if (value) {
if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_ADDDATA) {
resultCode=_storeDataPoints(xo, value, dataPoints, numberOfPoints);
if (resultCode==AQH_MSGDATA_RESULT_SUCCESS)
_sendDataChangedMsgToAllClients(xo, ep, value, dataPoints, numberOfPoints);
}
else {
DBG_INFO(NULL, "No permissions to add data to value \"%s\"", valueName);
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
}
}
else {
DBG_INFO(NULL, "No permissions to add/create value \"%s\"", valueName);
resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS;
}
}
else {
DBG_INFO(NULL, "No datapoints");
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
}
AQH_Value_free(recvdValue);
}
else {
DBG_INFO(NULL, "No value");
resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID;
}
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID,
AQH_IPC_PROTOCOL_DATA_VERSION,
AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}
}
int _storeDataPoints(AQHOME_SERVER *xo, 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(xo->storage, AQH_Value_GetId(v), timestamp, u.f);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return AQH_MSGDATA_RESULT_ERROR_GENERIC;
}
else {
DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
}
} /* for */
return AQH_MSGDATA_RESULT_SUCCESS;
}
void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc, const AQH_VALUE *v, const uint64_t *dataPoints, int numValues)
{
AQH_OBJECT *ep;
ep=AQH_Object_List_First(xo->tcpClientList);
while(ep) {
if (ep!=epSrc) {
if (AQH_Endpoint_GetFlags(ep) & AQH_ENDPOINT_FLAGS_WANTUPDATES) {
AQH_MESSAGE *msg;
DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint");
msg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
AQH_Endpoint_GetNextMessageId(ep), 0,
v, dataPoints, numValues);
AQH_Endpoint_AddMsgOut(ep, msg);
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint doesn't want updates");
}
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Not sending update msg to source of updates");
}
ep=AQH_Object_List_Next(ep);
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_UPDATEDATA_H
#define AQHOME_DATA_S_UPDATEDATA_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

857
apps/aqhome-data/server.c Normal file
View File

@@ -0,0 +1,857 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server_p.h"
#include "./s_connect.h"
#include "./s_getdevices.h"
#include "./s_getvalues.h"
#include "./s_addvalue.h"
#include "./s_annvalue.h"
#include "./s_updatedata.h"
#include "./s_setdata.h"
#include "./s_getdatapoints.h"
#include "./s_moddevice.h"
#include <aqhome/aqhome.h>
#include <aqhome/ipc2/ipc_server.h>
#include <aqhome/ipc2/tcpd_object.h>
#include <aqhome/msg/ipc/m_ipc.h>
#include <aqhome/msg/ipc/m_ipc_result.h>
#include <aqhome/msg/ipc/m_ipc_tag16.h>
#include <aqhome/msg/ipc/data/m_ipcd.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define I18N(msg) msg
#define I18S(msg) msg
enum {
AQH_AQHOME_SERVER_SLOT_NEWCLIENT=1,
AQH_AQHOME_SERVER_SLOT_CLOSED
};
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
GWEN_INHERIT(AQH_OBJECT, AQHOME_SERVER)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static int _setupStorage(AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs);
static int _setupIpc(AQH_OBJECT *o, AQHOME_SERVER *xo, GWEN_DB_NODE *dbArgs);
static int _handleSignal(AQH_OBJECT *o, uint32_t slotId, AQH_OBJECT *senderObject, int param1, void *param2);
static int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
static int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
static void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep);
static void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
static AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName);
static int _createPidFile(const char *pidFilename);
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
/* ------------------------------------------------------------------------------------------------
* implementation
* ------------------------------------------------------------------------------------------------
*/
AQH_OBJECT *AqHomeDataServer_new(AQH_EVENT_LOOP *eventLoop)
{
AQH_OBJECT *o;
AQHOME_SERVER *xo;
o=AQH_Object_new(eventLoop);
GWEN_NEW_OBJECT(AQHOME_SERVER, xo);
GWEN_INHERIT_SETDATA(AQH_OBJECT, AQHOME_SERVER, o, xo, _freeData);
xo->storageMutex=GWEN_Mutex_new();
xo->tcpClientList=AQH_Object_List_new();
xo->requestTree=AQH_MsgRequest_new();
AQH_Object_SetSignalHandlerFn(o, _handleSignal);
return o;
}
void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
{
AQHOME_SERVER *xo;
xo=(AQHOME_SERVER*) p;
GWEN_Mutex_free(xo->storageMutex);
if (xo->ipcServer) {
AQH_Object_Disable(xo->ipcServer);
AQH_Object_free(xo->ipcServer);
xo->ipcServer=NULL;
}
if (xo->tcpClientList) {
AQH_Object_List_free(xo->tcpClientList);
xo->tcpClientList=NULL;
}
GWEN_DB_Group_free(xo->dbArgs);
AQH_Storage_free(xo->storage);
free(xo->pidFile);
GWEN_FREE_OBJECT(xo);
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* getters, setters
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
AQHOME_SERVER *AqHomeDataServer_GetServerData(const AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
return xo;
}
int AqHomeDataServer_GetTimeout(const AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo)
return xo->timeout;
return 0;
}
int AqHomeDataServer_GetClientNum(const AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo)
return AQH_Object_List_GetCount(xo->tcpClientList);
return 0;
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* init
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
int AqHomeDataServer_Init(AQH_OBJECT *o, int argc, char **argv)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
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");
xo->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);
}
xo->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_DATA_DEFAULT_PIDFILE);
if (s && *s) {
free(xo->pidFile);
xo->pidFile=strdup(s);
rv=_createPidFile(s);
if (rv<0) {
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
return rv;
}
}
rv=_setupStorage(xo, dbArgs);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
rv=_setupIpc(o, xo, dbArgs);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
return 0;
}
else {
DBG_ERROR(NULL, "Not of type AQHOME_SERVER object");
return GWEN_ERROR_INVALID;
}
}
int _setupStorage(AQHOME_SERVER *xo, 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;
}
xo->storage=sto;
return 0;
}
int _setupIpc(AQH_OBJECT *o, AQHOME_SERVER *xo, 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) {
int fd;
DBG_ERROR(NULL, "Starting TCP service on \"%s\":%d", tcpAddress, tcpPort);
fd=AQH_TcpdObject_CreateListeningSocket(tcpAddress, tcpPort);
if (fd<0) {
DBG_INFO(NULL, "here");
return GWEN_ERROR_IO;
}
xo->ipcServer=AQH_IpcServerObject_new(AQH_Object_GetEventLoop(o), fd);
AQH_Object_AddLink(xo->ipcServer, AQH_IPC_SERVER_SIGNAL_NEWCLIENT, AQH_AQHOME_SERVER_SLOT_NEWCLIENT, o);
AQH_Object_Enable(xo->ipcServer);
return 0;
}
else {
DBG_ERROR(NULL, "Missing server address");
return GWEN_ERROR_GENERIC;
}
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* signal handler
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
int _handleSignal(AQH_OBJECT *o, uint32_t slotId, AQH_OBJECT *senderObject, GWEN_UNUSED int param1, void *param2)
{
switch(slotId) {
case AQH_AQHOME_SERVER_SLOT_NEWCLIENT: return _handleNewClient(o, (AQH_OBJECT*) param2);
case AQH_AQHOME_SERVER_SLOT_CLOSED: return _handleClientDown(o, senderObject);
default:
break;
}
return 0; /* not handled */
}
int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
DBG_ERROR(NULL, "New IPC client");
AQH_Object_AddLink(clientEndpoint, AQH_ENDPOINT_SIGNAL_CLOSED, AQH_AQHOME_SERVER_SLOT_CLOSED, o);
AQH_Object_List_Add(clientEndpoint, xo->tcpClientList);
return 1; /* handled */
}
return 0; /* not handled */
}
int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
DBG_ERROR(NULL, "IPC client down");
AQH_Object_AddFlags(clientEndpoint, AQH_OBJECT_FLAGS_DELETE);
return 1; /* handled */
}
return 0; /* not handled */
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* client management functions
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
void AqHomeDataServer_CleanupClients(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
AQH_OBJECT *ep;
ep=AQH_Object_List_First(xo->tcpClientList);
while(ep) {
AQH_OBJECT *epNext;
epNext=AQH_Object_List_Next(ep);
if (AQH_Object_GetFlags(ep) & AQH_OBJECT_FLAGS_DELETE) {
AQH_Object_List_Del(ep);
AQH_Object_free(ep);
}
ep=epNext;
} /* while */
}
}
void AqHomeDataServer_HandleClientMsgs(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
AQH_OBJECT *ep;
ep=AQH_Object_List_First(xo->tcpClientList);
while(ep) {
AQH_OBJECT *epNext;
epNext=AQH_Object_List_Next(ep);
_handleMsgsFromClient(o, xo, ep);
ep=epNext;
} /* while */
}
}
void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep)
{
AQH_MESSAGE *msg;
while( (msg=AQH_Endpoint_GetNextMsgIn(ep)) ) {
AQH_Message_SetObject(msg, ep);
if (AQH_Request_Tree2_HandleIpcMsg(xo->requestTree, ep, msg)!=AQH_MSG_REQUEST_RESULT_HANDLED)
_handleMsgFromClient(o, ep, msg);
AQH_Message_free(msg);
}
}
void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg)
{
GWEN_TAG16_LIST *tagList;
uint16_t code;
uint8_t protoId;
tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0);
code=AQH_IpcMessage_GetCode(msg);
protoId=AQH_IpcMessage_GetProtoId(msg);
if (protoId==AQH_IPC_PROTOCOL_DATA_ID) {
DBG_INFO(NULL, "Received IPC packet %d (%x)", (int) code, code);
switch(code) {
case AQH_MSGTYPE_IPC_DATA_CONNECT_REQ: AqHomeDataServer_HandleConnect(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: AqHomeDataServer_HandleUpdateData(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeDataServer_HandleGetValues(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: AqHomeDataServer_HandleGetDataPoints(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeDataServer_HandleSetData(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_ADDVALUE: AqHomeDataServer_HandleAddValue(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE: AqHomeDataServer_HandleAnnounceValue(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ: AqHomeDataServer_HandleGetDevices(o, ep, msg, tagList); break;
case AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ: AqHomeDataServer_HandleModDevice(o, ep, msg, tagList); break;
default: break;
}
}
else {
DBG_ERROR(NULL, "Invalid IPC protocol %d (%02x)", protoId, protoId);
}
GWEN_Tag16_List_free(tagList);
}
AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
AQH_OBJECT *ep;
ep=AQH_Object_List_First(xo->tcpClientList);
while(ep) {
const char *s;
s=AQH_Endpoint_GetServiceName(ep);
if (s && *s && strcasecmp(s, serviceName)==0)
return ep;
ep=AQH_Object_List_Next(ep);
}
}
return NULL;
}
void AqHomeDataServer_SendResponseResultToEndpoint(AQH_OBJECT *ep, uint32_t refMsgId, int result)
{
AQH_MESSAGE *msg;
msg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep), refMsgId, result, NULL);
AQH_Endpoint_AddMsgOut(ep, msg);
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* request management functions
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
AQH_MSG_REQUEST *AqHomeDataServer_GetRequestTree(const AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo)
return xo->requestTree;
return NULL;
}
void AqHomeDataServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo && rq)
AQH_MsgRequest_Tree2_AddChild(xo->requestTree, rq);
}
void AqHomeDataServer_CleanupRequests(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
AQH_Request_Tree2_CheckTimeouts(xo->requestTree);
AQH_Request_Tree2_Cleanup(xo->requestTree);
}
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* storage management functions
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
int AqHomeDataServer_LockStorage(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
int rv;
rv=GWEN_Mutex_Lock(xo->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex");
return rv;
}
return rv;
}
return GWEN_ERROR_INVALID;
}
int AqHomeDataServer_UnlockStorage(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
int rv;
rv=GWEN_Mutex_Unlock(xo->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex");
return rv;
}
return rv;
}
return GWEN_ERROR_INVALID;
}
int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
if (AQH_Storage_GetRuntimeFlags(xo->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) {
int rv;
DBG_INFO(NULL, "Storage modified, writing statefile");
rv=AqHomeDataServer_LockStorage(o);
if (rv<0) {
DBG_INFO(NULL, "Error locking storage (%d)", rv);
return rv;
}
rv=AQH_Storage_WriteState(xo->storage);
if (rv<0) {
DBG_INFO(NULL, "Error writing state file (%d)", rv);
AqHomeDataServer_UnlockStorage(o);
return rv;
}
rv=AqHomeDataServer_UnlockStorage(o);
if (rv<0) {
DBG_INFO(NULL, "Error unlocking storage (%d)", rv);
return rv;
}
}
return 0;
}
return GWEN_ERROR_INVALID;
}
AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
const char *serviceName;
AQH_VALUE *v;
GWEN_BUFFER *buf;
const char *valueName;
const char *deviceName;
serviceName=AQH_Endpoint_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(xo->storage, GWEN_Buffer_GetStart(buf));
if (v==NULL) {
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE) {
AQH_DEVICE *device;
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf));
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, 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(xo->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;
}
return NULL;
}
AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName)
{
const char *serviceName;
AQH_DEVICE *device;
GWEN_BUFFER *buf;
serviceName=AQH_Endpoint_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(xo->storage, GWEN_Buffer_GetStart(buf));
if (device==NULL) {
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_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(xo->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;
}
/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* helper functions
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
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;
}

59
apps/aqhome-data/server.h Normal file
View File

@@ -0,0 +1,59 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_SERVER_H
#define AQHOME_DATA_SERVER_H
#include <aqhome/data/storage.h>
#include <aqhome/events2/object.h>
#include <aqhome/ipc2/msgrequest.h>
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
AQH_OBJECT *AqHomeDataServer_new(AQH_EVENT_LOOP *eventLoop);
int AqHomeDataServer_Init(AQH_OBJECT *o, int argc, char **argv);
int AqHomeDataServer_GetTimeout(const AQH_OBJECT *o);
int AqHomeDataServer_GetClientNum(const AQH_OBJECT *o);
void AqHomeDataServer_CleanupClients(AQH_OBJECT *o);
void AqHomeDataServer_HandleClientMsgs(AQH_OBJECT *o);
AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName);
void AqHomeDataServer_SendResponseResultToEndpoint(AQH_OBJECT *ep, uint32_t refMsgId, int result);
AQH_MSG_REQUEST *AqHomeDataServer_GetRequestTree(const AQH_OBJECT *o);
void AqHomeDataServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq);
void AqHomeDataServer_CleanupRequests(AQH_OBJECT *o);
int AqHomeDataServer_LockStorage(AQH_OBJECT *o);
int AqHomeDataServer_UnlockStorage(AQH_OBJECT *o);
int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o);
AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate);
#endif

View File

@@ -0,0 +1,49 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_SERVER_P_H
#define AQHOME_DATA_SERVER_P_H
#include "./server.h"
#include <aqhome/events2/object.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"
typedef struct AQHOME_SERVER AQHOME_SERVER;
struct AQHOME_SERVER {
AQH_OBJECT *ipcServer;
AQH_OBJECT_LIST *tcpClientList;
AQH_MSG_REQUEST *requestTree;
GWEN_DB_NODE *dbArgs;
AQH_STORAGE *storage;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
GWEN_MUTEX *storageMutex;
};
AQHOME_SERVER *AqHomeDataServer_GetServerData(const AQH_OBJECT *o);
#endif

View File

@@ -0,0 +1,81 @@
<?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" >
xmlread.h
xmlwrite.h
server.h
server_p.h
s_publish.h
s_setdata.h
</headers>
<sources>
$(local/typefiles)
main.c
xmlread.c
xmlwrite.c
server.c
s_publish.c
s_setdata.c
</sources>
<useTargets>
aqhome
aqhmqtt_types
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
types
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

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

@@ -0,0 +1,282 @@
/****************************************************************************
* 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 "./server.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 CONNCHECK_INTERVAL_IN_SECS 10
#define PING_INTERVAL_IN_SECS 120
#define SAVE_INTERVAL_IN_SECS 60
#define FULL_DEBUG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
static int _diffInSeconds(time_t t1, time_t t0);
#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, saPIPE;
#endif
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_OBJECT *aqh;
int rv;
GWEN_GUI *gui;
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);
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Notice);
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);
eventLoop=AQH_EventLoop_new();
aqh=AQH_MqttLogServer_new(eventLoop);
rv=AQH_MqttLogServer_Init(aqh, argc, argv);
if (rv<0) {
if (rv==GWEN_ERROR_CLOSE)
return 1;
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh, eventLoop);
AQH_MqttLogServer_Fini(aqh);
AQH_Object_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
{
time_t timeStart;
int timeout;
time_t timeLastConnCheck;
time_t timeLastSave;
time_t timeLastPingSend;
int rv;
timeout=AQH_MqttLogServer_GetTimeout(aqh);
timeStart=time(NULL);
timeLastConnCheck=time(NULL);
timeLastSave=time(NULL);
timeLastPingSend=time(NULL);
while(!stopService) {
time_t now;
AQH_EventLoop_Run(eventLoop, 2000);
AQH_MqttLogServer_HandleMqttMsgs(aqh);
AQH_MqttLogServer_HandleBrokerMsgs(aqh);
now=time(NULL);
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Check connections");
AQH_MqttLogServer_CheckBrokerConnection(aqh);
AQH_MqttLogServer_CheckMqttConnection(aqh);
timeLastConnCheck=now;
}
if (_diffInSeconds(now, timeLastPingSend)>PING_INTERVAL_IN_SECS) {
rv=AQH_MqttLogServer_SendPing(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error sending PING");
}
timeLastPingSend=time(NULL);
}
if (_diffInSeconds(now, timeLastSave)>SAVE_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Writing device files");
rv=AQH_MqttLogServer_SaveRuntimeDeviceFiles(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error writing device file");
}
timeLastSave=time(NULL);
}
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
DBG_INFO(NULL, "Timeout");
break;
}
} /* while */
DBG_ERROR(NULL, "Leaving server");
rv=AQH_MqttLogServer_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;
rv=_setupSigAction(&saPIPE, SIGPIPE);
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;
case SIGPIPE:
DBG_WARN(0, "Received PIPE signal");
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}
int _diffInSeconds(time_t t1, time_t t0)
{
return t1-t0;
}

View File

@@ -0,0 +1,449 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_publish.h"
#include "./server_p.h"
#include <aqhome/aqhome.h>
#include <aqhome/msg/ipc/data/m_ipcd.h>
#include <aqhome/msg/ipc/data/m_ipcd_multidata.h>
#include <aqhome/msg/ipc/data/m_ipcd_values.h>
#include <aqhome/ipc2/endpoint.h>
#include <aqhome/msg/mqtt/m_mqtt_publish.h>
#include <gwenhywfar/json_read.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _handlePublish(AQH_OBJECT *o, const char *rcvdTopic, const char *rcvdValue);
static void _handleNumTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
AQHMQTT_TOPIC *topic, const char *rcvdValue);
static void _handleJsonTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
AQHMQTT_TOPIC *topic, const char *rcvdValue);
static void _sendMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue);
static void _announceDeviceToBroker(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device);
static void _sendAnnounceValueMessage(AQH_MQTTLOG_SERVER *xo, 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(AQH_MQTTLOG_SERVER *xo, const char *rcvdTopic);
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);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_MqttLogServer_HandlePublishMsg(AQH_OBJECT *o, GWEN_UNUSED AQH_OBJECT *ep, const AQH_MESSAGE *msg)
{
if (o && msg) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo && xo->registeredDeviceList) {
char *topic;
char *value;
topic=AQH_MqttMessagePublish_ExtractTopic(msg);
value=AQH_MqttMessagePublish_ExtractValue(msg);
if (topic && value) {
int rv;
rv=_handlePublish(o, topic, value);
if (rv!=1) {
DBG_INFO(NULL, "New topic \"%s\", trying to register", topic);
rv=_registerNewDeviceForTopic(xo, topic);
if (rv==1) {
rv=_handlePublish(o, 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(AQH_OBJECT *o, const char *rcvdTopic, const char *rcvdValue)
{
if (o && rcvdTopic && *rcvdTopic) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo && xo->registeredDeviceList) {
AQHMQTT_DEVICE *device;
device=AQHMQTT_Device_List_First(xo->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(xo, device, topic, rcvdValue);
else
_handleNumTopic(xo, device, topic, rcvdValue);
return 1;
}
}
device=AQHMQTT_Device_List_Next(device);
}
DBG_INFO(AQH_LOGDOMAIN, "ignoring topic \"%s\"", rcvdTopic);
}
}
return 0;
}
void _handleNumTopic(AQH_MQTTLOG_SERVER *xo, AQHMQTT_DEVICE *device,
AQHMQTT_TOPIC *topic, const char *rcvdValue)
{
AQHMQTT_VALUE_LIST *valueList;
valueList=AQHMQTT_Topic_GetValueList(topic);
if (valueList)
_sendMessage(xo, device, AQHMQTT_Value_List_First(valueList), rcvdValue);
else {
DBG_INFO(NULL, "No value list in device \"%s\"", AQHMQTT_Device_GetId(device));
}
}
void _handleJsonTopic(AQH_MQTTLOG_SERVER *xo, 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(xo, device, value, s);
}
}
value=AQHMQTT_Value_List_Next(value);
} /* while */
}
GWEN_JsonElement_free(jeRoot);
}
}
void _sendMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value, const char *rcvdValue)
{
int rv;
double f;
const char *deviceName;
deviceName=AQHMQTT_Device_GetId(device);
rv=GWEN_Text_StringToDouble(rcvdValue, &f);
if (rv<0) {
DBG_ERROR(NULL, "Invalid value received from MQTT server (%s)", rcvdValue?rcvdValue:"<empty>");
}
else {
uint64_t now;
AQH_VALUE *msgValue;
now=(uint64_t) time(NULL);
msgValue=_mkMessageValue(device, value);
if (xo->brokerEndpoint) {
AQH_MESSAGE *pubMsg;
pubMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
msgValue, now, f);
DBG_INFO(AQH_LOGDOMAIN, "BROKER UPDATE_DATA %s/%s: %f",
deviceName?deviceName:"<no device name>",
AQH_Value_GetName(msgValue), f);
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, pubMsg);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Skipping BROKER UPDATE_DATA %s/%s: %f",
deviceName?deviceName:"<no device name>",
AQH_Value_GetName(msgValue), f);
}
AQH_Value_free(msgValue);
}
}
void _announceDeviceToBroker(AQH_MQTTLOG_SERVER *xo, 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(xo, device, value);
value=AQHMQTT_Value_List_Next(value);
}
}
topic=AQHMQTT_Topic_List_Next(topic);
}
}
}
void _sendAnnounceValueMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQHMQTT_VALUE *value)
{
AQH_MESSAGE *pubMsg;
AQH_VALUE *msgValue;
msgValue=_mkMessageValue(device, value);
if (xo->brokerEndpoint) {
pubMsg=AQH_IpcdMessageValues_newForOne(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
0, msgValue);
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN, "BROKER ANNOUNCE_VALUE %s", AQH_Value_GetName(msgValue));
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, pubMsg);
}
AQH_Value_free(msgValue);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Ignoring BROKER ANNOUNCE_VALUE %s", AQH_Value_GetName(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(AQH_MQTTLOG_SERVER *xo, const char *rcvdTopic)
{
if (rcvdTopic && *rcvdTopic) {
if (xo->availableDeviceList) {
AQHMQTT_DEVICE *device;
device=AQHMQTT_Device_List_First(xo->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 (xo->registeredDeviceList==NULL)
xo->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, xo->registeredDeviceList);
_announceDeviceToBroker(xo, 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

@@ -0,0 +1,25 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_PUBLISH_H
#define AQHOMEMQTT_S_PUBLISH_H
#include "./aqhome_mqtt.h"
#include <aqhome/events2/object.h>
#include <aqhome/ipc2/message.h>
void AQH_MqttLogServer_HandlePublishMsg(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
#endif

View File

@@ -0,0 +1,180 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./s_setdata.h"
#include "./server_p.h"
#include "aqhome/data/value.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/msg/mqtt/m_mqtt_publish.h"
#include "aqhome/ipc2/endpoint.h"
#include <gwenhywfar/debug.h>
#define DEBUG_DRY_RUN 1 /* don't actually set value if "1" */
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device,
const char *valueName, const char *valueData);
static void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId,
const AQHMQTT_TOPIC *topic, const char *valueData);
static GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o,
const AQH_MESSAGE *msg,
const GWEN_TAG16_LIST *tagList)
{
if (o && msg) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo) {
AQH_VALUE *recvdValue;
DBG_ERROR(NULL, "Received SETDATA request");
recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
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=AQH_MqttLogServer_FindRegisteredDevice(o, deviceName);
if (device) {
char *valueDataFreeable;
DBG_ERROR(NULL, "Sending data to value \"%s\" of device \"%s\"", valueName, deviceName);
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
_sendDataForDevice(xo, device, valueName, valueDataFreeable);
free(valueDataFreeable);
}
else {
DBG_ERROR(NULL, "Device \"%s\" not found", deviceName);
AQH_MqttLogServer_DumpRegisteredDevices(o);
}
}
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(AQH_MQTTLOG_SERVER *xo,
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(xo, deviceId, topic, valueData);
}
} /* if out */
topic=AQHMQTT_Topic_List_Next(topic);
} /* while topic */
}
}
else {
DBG_ERROR(NULL, "Device has no id");
}
}
void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData)
{
GWEN_BUFFER *buf;
#if !DEBUG_DRY_RUN
AQH_MESSAGE *msgOut;
#endif
buf=_createBufferForTopic(deviceId, topic);
#if !DEBUG_DRY_RUN
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
msgOut=AQH_MqttMessagePublish_new(0, 0, GWEN_Buffer_GetStart(buf),
(const uint8_t*) (valueData?valueData:NULL),
valueData?strlen(valueData):0);
if (msgOut)
AQH_Endpoint_AddMsgOut(xo->mqttEndpoint, msgOut);
else {
DBG_ERROR(NULL, "Error creating message");
}
#else
DBG_ERROR(NULL, "Would MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
#endif
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,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_S_SETDATA_H
#define AQHOMEMQTT_S_SETDATA_H
#include "./aqhome_mqtt.h"
#include <aqhome/events2/object.h>
#include <aqhome/ipc2/message.h>
#include <gwenhywfar/tag16.h>
void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
#endif

1143
apps/aqhome-mqttlog/server.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 SERVER_H
#define SERVER_H
#include "aqhome-mqttlog/types/device.h"
#include "aqhome/events2/object.h"
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
AQH_OBJECT *AQH_MqttLogServer_new(AQH_EVENT_LOOP *eventLoop);
int AQH_MqttLogServer_Init(AQH_OBJECT *o, int argc, char **argv);
void AQH_MqttLogServer_Fini(AQH_OBJECT *o);
void AQH_MqttLogServer_ReloadDeviceFiles(AQH_OBJECT *o);
void AQH_MqttLogServer_LoadRuntimeDeviceFiles(AQH_OBJECT *o);
int AQH_MqttLogServer_SaveRuntimeDeviceFiles(AQH_OBJECT *o);
/* loop functions */
void AQH_MqttLogServer_HandleBrokerMsgs(AQH_OBJECT *o);
void AQH_MqttLogServer_HandleMqttMsgs(AQH_OBJECT *o);
void AQH_MqttLogServer_CheckBrokerConnection(AQH_OBJECT *o);
void AQH_MqttLogServer_CheckMqttConnection(AQH_OBJECT *o);
int AQH_MqttLogServer_SendPing(AQH_OBJECT *o);
/* getters and setters */
int AQH_MqttLogServer_GetTimeout(const AQH_OBJECT *o);
void AQH_MqttLogServer_SetPidFile(AQH_OBJECT *o, const char *s);
void AQH_MqttLogServer_SetDeviceFile(AQH_OBJECT *o, const char *s);
/* device management */
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_GetAvailableDeviceList(const AQH_OBJECT *o);
void AQH_MqttLogServer_SetAvailableDeviceList(AQH_OBJECT *o, AQHMQTT_DEVICE_LIST *dl);
void AQH_MqttLogServer_SetRegisteredDeviceList(AQH_OBJECT *o, AQHMQTT_DEVICE_LIST *dl);
AQHMQTT_DEVICE *AQH_MqttLogServer_FindRegisteredDevice(AQH_OBJECT *o, const char *wantedDeviceId);
void AQH_MqttLogServer_DumpRegisteredDevices(const AQH_OBJECT *o);
#endif

View File

@@ -0,0 +1,67 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 SERVER_P_H
#define SERVER_P_H
#include "./server.h"
#include "aqhome-nodes/types/device.h"
#include "aqhome/ipc2/msgrequest.h"
#include <termios.h>
/* default values */
#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"
typedef struct AQH_MQTTLOG_SERVER AQH_MQTTLOG_SERVER;
struct AQH_MQTTLOG_SERVER {
AQH_OBJECT *mqttEndpoint;
AQH_OBJECT *brokerEndpoint;
GWEN_DB_NODE *dbArgs;
AQHMQTT_DEVICE_LIST *availableDeviceList;
AQHMQTT_DEVICE_LIST *registeredDeviceList;
char *deviceFile;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
char *mqttAddress;
int mqttPort;
char *mqttClientId;
int mqttKeepAlive;
char *brokerAddress;
int brokerPort;
char *brokerClientId;
time_t timestampMqttDown;
time_t timestampBrokerDown;
int timeoutInSeconds;
};
AQH_MQTTLOG_SERVER *AQH_MqttLogServer_GetServerData(const AQH_OBJECT *o);
#endif

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,482 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server_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(const GWEN_STRINGLIST *sl);
static int _readDeviceFileToList(const char *sFilename, AQHMQTT_DEVICE_LIST *deviceList);
static int _readXmlDevices(GWEN_XMLNODE *deviceListNode, AQHMQTT_DEVICE_LIST *deviceList);
static AQHMQTT_DEVICE *_readXmlDevice(GWEN_XMLNODE *deviceNode);
static AQHMQTT_TOPIC_LIST *_readXmlTopicList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TOPIC *_readXmlTopic(GWEN_XMLNODE *topicNode);
static AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode);
static AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode);
static AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode);
static AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename)
{
if (o) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo) {
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(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;
}
}
}
return NULL;
}
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o)
{
if (o) {
AQH_MQTTLOG_SERVER *xo;
xo=AQH_MqttLogServer_GetServerData(o);
if (xo) {
GWEN_STRINGLIST *sl;
sl=AQH_GetListOfMatchingDataFiles("aqhome/devices/mqtt", "*.xml");
if (sl) {
AQHMQTT_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;
}
AQHMQTT_DEVICE_LIST *_readDeviceFiles(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(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(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(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, 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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 2025 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/server.h"
#include "aqhome-mqttlog/aqhome_mqtt.h"
#include "aqhome-mqttlog/types/device.h"
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDataDeviceFiles(AQH_OBJECT *o);
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename);
#endif

View File

@@ -0,0 +1,249 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 AQH_MqttLogServer_WriteDevicesFile(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 2025 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/server.h"
#include "aqhome-mqttlog/aqhome_mqtt.h"
#include "aqhome-mqttlog/types/device.h"
int AQH_MqttLogServer_WriteDevicesFile(const AQHMQTT_DEVICE_LIST *deviceList, const char *sFilename);
#endif

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

@@ -0,0 +1,84 @@
<?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" >
db.h
tty_log.h
devicesread.h
devicesdump.h
r_setdata.h
r_connect.h
r_forward.h
r_setaccmsggrps.h
r_getnodes.h
</headers>
<sources>
$(local/typefiles)
server.c
db.c
devicesread.c
devicesdump.c
r_setdata.c
r_connect.c
r_forward.c
r_setaccmsggrps.c
r_getnodes.c
main.c
</sources>
<useTargets>
aqhnodes_types
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
</libraries>
<subdirs>
types
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

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

@@ -0,0 +1,390 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/node/m_node.h"
#include "aqhome/msg/node/m_sendstats.h"
#include "aqhome/msg/node/m_recvstats.h"
#include "aqhome/msg/node/m_value.h"
#include "aqhome/msg/node/m_addr.h"
#include "aqhome/msg/node/m_device.h"
#include "aqhome/msg/node/m_flashready.h"
#include "aqhome/data/value.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/ipc2/endpoint.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleMsgValue(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _handleAddressMsg(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _handleMsgComSendStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _handleMsgComRecvStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _handleMsgDevice(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _handleMsgFlashReady(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg, uint32_t uid);
static void _updateTimestampLastChange(AQH_NODE_INFO *ni);
static void _assignDeviceId(AQH_OBJECT *o, AQH_NODE_INFO *ni, uint32_t uid);
static void _announceNodeValues(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_NODE_INFO *ni);
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
static void _announceValue(AQH_NODE_SERVER *xo, uint32_t uid, const AQHNODE_VALUE *v);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_NodeMsgToDb(AQH_OBJECT *o, const AQH_MESSAGE *msg)
{
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
uint8_t msgType;
msgType=AQH_NodeMessage_GetMsgType(msg);
switch(msgType) {
case AQH_MSG_TYPE_COMSENDSTATS: _handleMsgComSendStat(xo, msg); break;
case AQH_MSG_TYPE_COMRECVSTATS: _handleMsgComRecvStat(xo, msg); break;
case AQH_MSG_TYPE_VALUE_REPORT: _handleMsgValue(xo, msg); break;
case AQH_MSG_TYPE_NEED_ADDRESS: _handleAddressMsg(xo, msg); break;
case AQH_MSG_TYPE_CLAIM_ADDRESS: _handleAddressMsg(xo, msg); break;
case AQH_MSG_TYPE_HAVE_ADDRESS: _handleAddressMsg(xo, msg); break;
case AQH_MSG_TYPE_DEVICE: _handleMsgDevice(o, xo, msg); break;
case AQH_MSG_TYPE_FLASH_READY: _handleMsgFlashReady(o, xo, msg); break;
default: break;
}
}
}
void AQH_NodeServer_WriteNodeDb(AQH_OBJECT *o)
{
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo && xo->dbFile) {
GWEN_DB_NODE *dbNodeDb;
AQH_NodeDb_ClearModified(xo->nodeDb);
dbNodeDb=GWEN_DB_Group_new("nodeDb");
AQH_NodeDb_toDb(xo->nodeDb, dbNodeDb);
GWEN_DB_WriteFile(dbNodeDb, xo->dbFile, GWEN_DB_FLAGS_DEFAULT);
GWEN_DB_Group_free(dbNodeDb);
}
}
void _handleMsgValue(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_ValueMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleAddressMsg(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_AddrMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _handleMsgComSendStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_SendStatsMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMessage_GetPacketsOut(msg));
AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMessage_GetCollisions(msg));
AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMessage_GetBusyErrors(msg));
AQH_NodeDb_SetModified(xo->nodeDb);
_updateTimestampLastChange(ni);
}
void _handleMsgComRecvStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_RecvStatsMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
AQH_NodeInfo_SetStatsPacketsIn(ni, AQH_RecvStatsMessage_GetPacketsIn(msg));
AQH_NodeInfo_SetStatsCrcErrors(ni, AQH_RecvStatsMessage_GetCrcErrors(msg));
AQH_NodeInfo_SetStatsIoErrors(ni, AQH_RecvStatsMessage_GetIoErrors(msg));
AQH_NodeDb_SetModified(xo->nodeDb);
_updateTimestampLastChange(ni);
}
void _handleMsgDevice(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_DeviceMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni) {
const char *s;
AQH_NodeInfo_SetManufacturer(ni, AQH_DeviceMessage_GetManufacturer(msg));
AQH_NodeInfo_SetDeviceType(ni, AQH_DeviceMessage_GetDeviceType(msg));
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_DeviceMessage_GetDeviceVersion(msg)<<8)+AQH_DeviceMessage_GetDeviceRevision(msg));
AQH_NodeInfo_SetFirmwareVersion(ni,
(AQH_DeviceMessage_GetFirmwareVariant(msg)<<24) |
(AQH_DeviceMessage_GetFirmwareVersionMajor(msg)<<16) |
(AQH_DeviceMessage_GetFirmwareVersionMinor(msg)<<8) |
AQH_DeviceMessage_GetFirmwareVersionPatchlevel(msg));
s=AQH_NodeInfo_GetDeviceId(ni);
if (!(s && *s))
_assignDeviceId(o, ni, uid);
_updateTimestampLastChange(ni);
AQH_NodeDb_SetModified(xo->nodeDb);
if (uid!=0x00000000L && uid!=0xffffffff)
_announceNodeValues(o, xo, ni);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
void _announceNodeValues(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_NODE_INFO *ni)
{
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(ni);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AQH_NodeServer_GetDeviceDefByName(o, 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(xo, 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(AQH_NODE_SERVER *xo, uint32_t uid, const AQHNODE_VALUE *v)
{
AQH_VALUE *value;
AQH_MESSAGE *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_IpcdMessageValues_newForOne(AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
0, value);
AQH_Endpoint_AddMsgOut(xo->brokerEndpoint, msg);
AQH_Value_free(value);
}
void _handleMsgFlashReady(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
AQH_NODE_INFO *ni;
uint32_t uid;
uid=AQH_FlashReadyMessage_GetUid(msg);
ni=_getOrCreateNodeAndUpdateUidAddr(xo, msg, uid);
if (ni) {
const char *s;
AQH_NodeInfo_SetManufacturer(ni, AQH_FlashReadyMessage_GetManufacturer(msg));
AQH_NodeInfo_SetDeviceType(ni, AQH_FlashReadyMessage_GetDeviceType(msg));
AQH_NodeInfo_SetDeviceVersion(ni, (AQH_FlashReadyMessage_GetDeviceVersion(msg)<<8)+AQH_FlashReadyMessage_GetDeviceRevision(msg));
AQH_NodeInfo_SetFirmwareVersion(ni,
(AQH_FlashReadyMessage_GetFirmwareVariant(msg)<<24) |
(AQH_FlashReadyMessage_GetFirmwareVersionMajor(msg)<<16) |
(AQH_FlashReadyMessage_GetFirmwareVersionMinor(msg)<<8) |
AQH_FlashReadyMessage_GetFirmwareVersionPatchlevel(msg));
s=AQH_NodeInfo_GetDeviceId(ni);
if (!(s && *s))
_assignDeviceId(o, ni, uid);
_updateTimestampLastChange(ni);
AQH_NodeDb_SetModified(xo->nodeDb);
if (uid!=0x00000000L && uid!=0xffffffff)
_announceNodeValues(o, xo, ni);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
}
AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg, uint32_t uid)
{
uint8_t busAddr;
AQH_NODE_INFO *ni;
busAddr=AQH_NodeMessage_GetSourceAddress(msg);
ni=AQH_NodeDb_GetNodeInfoByUid(xo->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(xo->nodeDb);
}
}
else {
int rv;
ni=AQH_NodeInfo_new();
AQH_NodeInfo_SetBusAddress(ni, busAddr);
AQH_NodeInfo_SetUid(ni, uid);
_updateTimestampLastChange(ni);
rv=AQH_NodeDb_AddNodeInfo(xo->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(AQH_OBJECT *o, AQH_NODE_INFO *ni, uint32_t uid)
{
const AQHNODE_DEVICE *dev;
dev=AQH_NodeServer_FindDeviceDef(o,
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);
}

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

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server.h"
#include "aqhome/events2/object.h"
#include "aqhome/ipc2/message.h"
void AQH_NodeServer_NodeMsgToDb(AQH_OBJECT *o, const AQH_MESSAGE *msg);
void AQH_NodeServer_WriteNodeDb(AQH_OBJECT *o);
#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 "./server_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 AQH_NodeServer_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 "./server.h"
#include "aqhome-nodes/types/device.h"
void AQH_NodeServer_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 "./server_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 *AQH_NodeServer_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 *AQH_NodeServer_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 2025 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 "./server.h"
#include "aqhome-nodes/types/device.h"
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDeviceFile(const char *sFilename);
AQHNODE_DEVICE_LIST *AQH_NodeServer_ReadDataDeviceFiles(void);
#endif

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

@@ -0,0 +1,251 @@
/****************************************************************************
* 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 "./server.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
#define CONNCLEAN_INTERVAL_IN_SECS 2
#define CONNCHECK_INTERVAL_IN_SECS 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
#ifdef HAVE_SIGNAL_H
static int _setSignalHandlers(void);
static int _setupSigAction(struct sigaction *sa, int sig);
static void _signalHandler(int s);
#endif
static int _diffInSeconds(time_t t1, time_t t0);
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
#ifdef HAVE_SIGNAL_H
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT, saPIPE;
#endif
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
int rv;
AQH_EVENT_LOOP *eventLoop;
AQH_OBJECT *aqh;
GWEN_GUI *gui;
rv=GWEN_Init();
if (rv) {
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
return 2;
}
GWEN_Logger_Open(0, "aqhome-nodes", 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);
eventLoop=AQH_EventLoop_new();
aqh=AQH_NodeServer_new(eventLoop);
rv=AQH_NodeServer_Init(aqh, argc, argv);
if (rv<0) {
if (rv==GWEN_ERROR_CLOSE)
return 1;
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh, eventLoop);
//AQH_NodeServer_Fini(aqh);
AQH_Object_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
{
time_t timeStart;
int timeout;
time_t timeLastConnectionCleanup;
time_t timeLastConnCheck;
timeout=AQH_NodeServer_GetTimeout(aqh);
timeStart=time(NULL);
timeLastConnectionCleanup=time(NULL);
timeLastConnCheck=time(NULL);
while(!stopService) {
time_t now;
AQH_EventLoop_Run(eventLoop, 2000);
AQH_NodeServer_HandleTtyMsgs(aqh);
AQH_NodeServer_HandleClientMsgs(aqh);
AQH_NodeServer_HandleBrokerMsgs(aqh);
now=time(NULL);
if (_diffInSeconds(now, timeLastConnectionCleanup)>CONNCLEAN_INTERVAL_IN_SECS) {
DBG_ERROR(NULL, "Cleanup connections");
AQH_NodeServer_CleanupClients(aqh);
timeLastConnectionCleanup=now;
}
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
DBG_ERROR(NULL, "Check connections");
AQH_NodeServer_CheckBrokerConnection(aqh);
AQH_NodeServer_CheckTtyConnection(aqh);
timeLastConnCheck=now;
}
if (timeout && (_diffInSeconds(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;
rv=_setupSigAction(&saPIPE, SIGPIPE);
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;
case SIGPIPE:
DBG_WARN(0, "Received PIPE signal");
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}
int _diffInSeconds(time_t t1, time_t t0)
{
return t1-t0;
}

View File

@@ -0,0 +1,82 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_connect.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleConnect(GWEN_UNUSED AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
if (tagList) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
char *clientId=NULL;
char *userId=NULL;
char *passw=NULL;
uint32_t flags;
clientId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_CLIENTID, NULL);
userId=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_USERID, NULL);
flags=AQH_Tag16_GetTagDataAsUint32(tagList, AQH_MSG_CONNECT_TAGS_FLAGS, 0);
passw=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSG_CONNECT_TAGS_PASSWORD, NULL);
if (clientId)
AQH_Endpoint_SetServiceName(ep, clientId);
if (userId)
AQH_Endpoint_SetUserName(ep, userId);
if (flags & AQH_MSG_CONNECT_FLAGS_WANTUPDATES)
AQH_Endpoint_AddFlags(ep, AQH_ENDPOINT_FLAGS_WANTUPDATES);
/* TODO: add user management, for now we allow all */
AQH_Endpoint_SetPermissions(ep,
AQH_ENDPOINT_PERMS_LISTVALUES |
AQH_ENDPOINT_PERMS_READVALUE |
AQH_ENDPOINT_PERMS_ADDVALUE |
AQH_ENDPOINT_PERMS_LISTDATA |
AQH_ENDPOINT_PERMS_READDATA |
AQH_ENDPOINT_PERMS_ADDDATA |
AQH_ENDPOINT_PERMS_LISTDEVICES |
AQH_ENDPOINT_PERMS_READDEVICE |
AQH_ENDPOINT_PERMS_ADDDEVICE |
AQH_ENDPOINT_PERMS_MODDEVICE);
free(passw);
free(userId);
free(clientId);
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
AQH_IPC_PROTOCOL_NODES_VERSION,
AQH_MSGTYPE_IPC_NODES_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_CONNECT_H
#define AQHOMED_R_CONNECT_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AQH_NodeServer_HandleConnect(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,79 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_forward.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
#include "aqhome/msg/ipc/nodes/m_ipcn_forward.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleForward(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
const GWEN_TAG16 *tag;
const uint8_t *ptr;
uint32_t len;
tag=tagList?GWEN_Tag16_List_FindFirstByTagType(tagList, AQH_MSGNODE_FORWARD_TAGS_MSG):NULL;
ptr=tag?GWEN_Tag16_GetTagData(tag):NULL;
len=tag?GWEN_Tag16_GetTagLength(tag):0;
if (ptr && len) {
if (xo->ttyEndpoint) {
AQH_MESSAGE *nodeMsg;
nodeMsg=AQH_Message_new();
AQH_Message_SetData(nodeMsg, ptr, len);
AQH_Message_SetUsedSize(nodeMsg, len);
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, nodeMsg);
}
else {
DBG_ERROR(NULL, "TTY endpoint currently not connected");
resultCode=AQH_MSGDATA_RESULT_ERROR_TRYAGAIN;
}
}
else {
DBG_ERROR(NULL, "Empty message to forward");
resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA;
}
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
AQH_IPC_PROTOCOL_NODES_VERSION,
AQH_MSGTYPE_IPC_NODES_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

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_FORWARD_H
#define AQHOMED_R_FORWARD_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AQH_NodeServer_HandleForward(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,77 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_getnodes.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
#include "aqhome/msg/ipc/nodes/m_ipcn_setaccmsggrps.h"
#include "aqhome/msg/ipc/nodes/m_ipcn_getdevices_rsp.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg)
{
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
AQH_NODE_INFO_LIST *nodeInfoList;
nodeInfoList=AQH_NodeDb_GetAllNodeInfos(xo->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;
AQH_MESSAGE *outMsg;
niNext=AQH_NodeInfo_List_Next(ni);
DBG_INFO(AQH_LOGDOMAIN, "Sending response for node %02x (%08x)", AQH_NodeInfo_GetBusAddress(ni), AQH_NodeInfo_GetUid(ni));
outMsg=AQH_IpcnMessageGetDevicesRsp_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP,
AQH_Endpoint_GetNextMessageId(ep), AQH_IpcMessage_GetMsgId(msg),
niNext?0:AQH_MSGNODE_GETDEVICES_RSP_FLAGS_LASTMSG, ni);
AQH_Endpoint_AddMsgOut(ep, outMsg);
DBG_ERROR(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
ni=niNext;
}
}
else {
AQH_MESSAGE *outMsg;
DBG_INFO(AQH_LOGDOMAIN, "No nodes");
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
AQH_IPC_PROTOCOL_NODES_VERSION,
AQH_MSGTYPE_IPC_NODES_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
AQH_MSGDATA_RESULT_ERROR_NODATA, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_GETNODES_H
#define AQHOMED_R_GETNODES_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
#endif

View File

@@ -0,0 +1,56 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_setaccmsggrps.h"
#include "./server_p.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
#include "aqhome/msg/ipc/nodes/m_ipcn_setaccmsggrps.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleSetAccMsgGrps(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
AQH_MESSAGE *outMsg;
int resultCode=AQH_MSGDATA_RESULT_SUCCESS;
uint32_t msgGrps=0;
msgGrps=AQH_IpcnMessageSetAcceptedMsgGroups_GetGroups(tagList);
AQH_Endpoint_SetAcceptedMsgGroups(ep, msgGrps);
outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_NODES_ID,
AQH_IPC_PROTOCOL_NODES_VERSION,
AQH_MSGTYPE_IPC_NODES_RESULT,
AQH_Endpoint_GetNextMessageId(ep),
AQH_IpcMessage_GetMsgId(msg),
resultCode, NULL);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

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_SETACCMSGGRPS_H
#define AQHOMED_R_SETACCMSGGRPS_H
#include "./server.h"
#include <gwenhywfar/tag16.h>
void AQH_NodeServer_HandleSetAccMsgGrps(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,382 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/data/value.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/msg/node/m_node.h"
#include "aqhome/msg/node/m_value.h"
#include "aqhome/ipc2/endpoint.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(AQH_NODE_SERVER *xo, const AQH_VALUE *value);
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
AQH_OBJECT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
static void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg)
{
if (o) {
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
uint32_t msgId;
DBG_INFO(NULL, "Received IPC SetDataRequest message");
msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
if (xo->ttyEndpoint) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0);
if (tagList) {
AQH_VALUE *value;
value=AQH_IpcdMessageSetData_ReadValue(tagList);
if (value) {
const char *varName;
varName=AQH_Value_GetName(value);
if (varName) {
char *data;
data=AQH_IpcdMessageSetData_ReadData(tagList);
if (data) {
AQH_NODE_INFO *nodeInfo;
nodeInfo=_getNodeInfoFromValue(xo, value);
if (nodeInfo) {
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AQH_NodeServer_GetDeviceDefByName(o, 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) {
AQH_MSG_REQUEST *rq;
int destAddr;
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
DBG_DEBUG(NULL, "Creating SETDATA request");
rq=_mkRequest_SetData(o, xo, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
AQH_NodeServer_AddRequestToTree(o, rq);
/* done */
}
else {
DBG_ERROR(NULL, "Bad data \"%s\"", data);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
else {
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_TRYAGAIN);
}
}
else {
DBG_ERROR(NULL, "No matching nodeinfo");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
free(data);
}
else {
DBG_ERROR(NULL, "No data");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
}
}
else {
DBG_ERROR(NULL, "No var name");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
}
AQH_Value_free(value);
}
else {
DBG_ERROR(NULL, "Could not read value from message");
}
GWEN_Tag16_List_free(tagList);
}
}
else {
DBG_ERROR(NULL, "TTY endpoint not connected");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_IO);
}
}
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
AQH_OBJECT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
AQH_MSG_REQUEST *rq;
AQH_MSG_REQUEST *subRq;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, ep);
AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
subRq=_mkSubRequest_SetData(o, xo, destAddr, valueId, dataVal, dataDenom);
AQH_MsgRequest_Tree2_AddChild(rq, subRq);
return rq;
}
void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
int result;
DBG_INFO(NULL, "SubRequest finished (reason: %d)", reason);
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
result=AQH_MsgRequest_GetResult(subRq);
if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
else
_sendResponseResultToBroker(ep, refMsgId, result);
AQH_MsgRequest_SetResult(rq, result);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
}
void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
/* ------------------------------------------------------------------------------------------------
* TTY Request SETDATA
*/
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
AQH_MSG_REQUEST *rq;
uint16_t msgId;
AQH_MESSAGE *msgOut;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, xo->ttyEndpoint);
AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);
msgId=AQH_Endpoint_GetNextMessageId(xo->ttyEndpoint) & 0xffff;
AQH_MsgRequest_SetRequestMsgId(rq, msgId);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
msgOut=AQH_ValueMessage_new(xo->nodeAddress, destAddr, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom);
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, msgOut);
return rq;
}
int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
{
AQH_OBJECT *o;
DBG_DEBUG(NULL, "Checking message from %02x", AQH_NodeMessage_GetSourceAddress(msg));
o=(AQH_OBJECT*)AQH_MsgRequest_GetPrivateData(rq);
if (o) {
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
uint8_t destAddr;
destAddr=AQH_NodeMessage_GetDestAddress(msg);
if (destAddr==0xff || destAddr==xo->nodeAddress) {
uint8_t msgCode;
msgCode=AQH_NodeMessage_GetMsgType(msg);
if (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK || msgCode==AQH_MSG_TYPE_VALUE_SET_NACK) {
uint16_t msgId;
msgId=AQH_ValueMessage_GetMsgId(msg);
if (msgId==AQH_MsgRequest_GetRequestMsgId(rq)) {
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL,
"Received response (%02x) for msg id %04x from %02x",
msgCode, msgId, AQH_NodeMessage_GetSourceAddress(msg));
AQH_MsgRequest_SetResult(rq,
(msgCode==AQH_MSG_TYPE_VALUE_SET_ACK)?AQH_MSGDATA_RESULT_SUCCESS:AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);
return AQH_MSG_REQUEST_RESULT_HANDLED;
}
else {
DBG_INFO(NULL, " Non-matching message id");
}
}
else {
DBG_INFO(NULL, " Non-matching message code");
}
}
}
}
return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
}
void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
AQH_NODE_INFO *_getNodeInfoFromValue(AQH_NODE_SERVER *xo, 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(xo->nodeDb, uid);
if (ni==NULL) {
DBG_ERROR(NULL, "Node \"%08lx\" not found", uid);
return NULL;
}
return ni;
}
return NULL;
}
void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result)
{
AQH_MESSAGE *msg;
msg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep), refMsgId, result, NULL);
AQH_Endpoint_AddMsgOut(ep, msg);
}

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 "./server.h"
#include <gwenhywfar/request.h>
void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg);
#endif

1443
apps/aqhome-nodes/server.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 SERVER_H
#define SERVER_H
#include "aqhome-nodes/types/device.h"
#include "aqhome/events2/object.h"
#include <aqhome/ipc2/msgrequest.h>
#define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001
#define AQH_ENDPOINT_PERMS_READVALUE 0x0002
#define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004
#define AQH_ENDPOINT_PERMS_LISTDATA 0x0010
#define AQH_ENDPOINT_PERMS_READDATA 0x0020
#define AQH_ENDPOINT_PERMS_ADDDATA 0x0040
#define AQH_ENDPOINT_PERMS_SETDATA 0x0080
#define AQH_ENDPOINT_PERMS_LISTDEVICES 0x0100
#define AQH_ENDPOINT_PERMS_READDEVICE 0x0200
#define AQH_ENDPOINT_PERMS_ADDDEVICE 0x0400
#define AQH_ENDPOINT_PERMS_MODDEVICE 0x0800
AQH_OBJECT *AQH_NodeServer_new(AQH_EVENT_LOOP *eventLoop);
int AQH_NodeServer_Init(AQH_OBJECT *o, int argc, char **argv);
void AQH_NodeServer_Fini(AQH_OBJECT *o);
/* loop functions */
void AQH_NodeServer_CleanupClients(AQH_OBJECT *o);
void AQH_NodeServer_HandleTtyMsgs(AQH_OBJECT *o);
void AQH_NodeServer_HandleClientMsgs(AQH_OBJECT *o);
void AQH_NodeServer_HandleBrokerMsgs(AQH_OBJECT *o);
void AQH_NodeServer_CheckBrokerConnection(AQH_OBJECT *o);
void AQH_NodeServer_CheckTtyConnection(AQH_OBJECT *o);
/* getters and setters */
int AQH_NodeServer_GetTimeout(const AQH_OBJECT *o);
void AQH_NodeServer_SetLogFile(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetDbFile(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetPidFile(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetDevicePath(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetTpcAddress(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetTcpPort(AQH_OBJECT *o, int i);
void AQH_NodeServer_SetBrokerAddress(AQH_OBJECT *o, const char *s);
void AQH_NodeServer_SetBrokerPort(AQH_OBJECT *o, int i);
void AQH_NodeServer_SetBrokerClientId(AQH_OBJECT *o, const char *s);
/* device management */
const AQHNODE_DEVICE_LIST *AQH_NodeServer_GetDeviceDefList(const AQH_OBJECT *o);
const AQHNODE_DEVICE *AQH_NodeServer_FindDeviceDef(const AQH_OBJECT *o,
uint32_t manufacturer,
uint16_t deviceType, uint16_t deviceVersion);
const AQHNODE_DEVICE *AQH_NodeServer_GetDeviceDefByName(const AQH_OBJECT *o, const char *name);
/* request management */
AQH_MSG_REQUEST *AQH_NodeServer_GetRequestTree(const AQH_OBJECT *o);
void AQH_NodeServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq);
void AQH_NodeServer_CleanupRequests(AQH_OBJECT *o);
#endif

View File

@@ -0,0 +1,80 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 SERVER_P_H
#define SERVER_P_H
#include "./server.h"
#include "aqhome-nodes/types/device.h"
#include "aqhome/nodes/nodedb.h"
#include "aqhome/ipc2/msgrequest.h"
#include <termios.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"
typedef struct AQH_NODE_SERVER AQH_NODE_SERVER;
struct AQH_NODE_SERVER {
AQH_OBJECT *ttyEndpoint;
AQH_OBJECT *brokerEndpoint;
AQH_OBJECT *ipcEndpoint;
AQH_OBJECT_LIST *ipcClientList;
AQH_NODE_DB *nodeDb;
AQHNODE_DEVICE_LIST *deviceDefList;
AQH_MSG_REQUEST *requestTree;
GWEN_DB_NODE *dbArgs;
char *dbFile;
char *logFile;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
char *devicePath;
char *tcpAddress;
int tcpPort;
char *brokerAddress;
int brokerPort;
char *brokerClientId;
time_t timestampTtyDown;
time_t timestampBrokerDown;
int nodeAddress;
struct termios initialTermiosState;
int timeoutInSeconds;
uint8_t protoId;
uint8_t protoVer;
};
AQH_NODE_SERVER *AQH_NodeServer_GetServerData(const AQH_OBJECT *o);
#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>

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

@@ -0,0 +1,145 @@
<?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" >
</headers>
<sources>
$(local/typefiles)
main.c
</sources>
<useTargets>
aqhomereact
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
-lm
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
<target type="ConvenienceLibrary" name="aqhomereact" >
<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" >
server.h
net_read.h
suntimes.h
</headers>
<sources>
$(local/typefiles)
server.c
net_read.c
suntimes.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,55 @@
<network id="tvlight1">
<units>
<unit id="valueFilterInPlug" type="valueFilter">
<params>
<param name="valueName">mqtt/plug02/powerusage</param>
</params>
</unit>
<unit id="suntime1" type="suntime" invert="TRUE">
<params>
<param name="latitude">52.619425</param>
<param name="longitude">10.087891</param>
<param name="offsetMinsForSunrise">0.0</param>
<param name="offsetMinsForSunset">-30.0</param>
</params>
</unit>
<unit id="delayedOff1" type="delayedOff">
<params>
<param name="delayTime">300</param>
<param name="threshold">70</param>
</params>
</unit>
<unit id="and1" type="and" />
<unit id="zeroPosNegString1" type="zeroPosNegString">
<params>
<param name="valueIfNegative">OFF</param>
<param name="valueIfZero">OFF</param>
<param name="valueIfPositive">ON</param>
</params>
</unit>
<unit id="valueSetOutPlug1" type="valueSet">
<params>
<param name="valueName">mqtt/109C2F/power</param>
</params>
</unit>
</units>
<links>
<!-- sourceUnit, sourcePort, targetUnit, targetPort -->
<link sourceUnit=".updatedValue" sourcePort="doubleOutput" targetUnit="valueFilterInPlug" targetPort="input" />
<link sourceUnit="valueFilterInPlug" sourcePort="output" targetUnit="delayedOff1" targetPort="input" />
<link sourceUnit=".timer" sourcePort="output" targetUnit="suntime1" targetPort="timer" />
<link sourceUnit="suntime1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="delayedOff1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="and1" sourcePort="output" targetUnit="zeroPosNegString1" targetPort="input" />
<link sourceUnit="zeroPosNegString1" sourcePort="output" targetUnit="valueSetOutPlug1" targetPort="input" />
</links>
</network>

View File

@@ -0,0 +1,8 @@
<?xml?>
<gwbuild>
<data dist="true">
001-tvlight.xml
</data>
</gwbuild>

356
apps/aqhome-react/main.c Normal file
View File

@@ -0,0 +1,356 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./server.h"
#include "./net_read.h"
#include "aqhome-react/units/u_timer.h"
#include "aqhome-react/types/prgrule.h"
//#include "aqhome-react/types/prgrule-t.h"
#include "aqhome/aqhome.h"
#include "aqhome/data/path-t.h"
#include "aqhome/data/vars-t.h"
//#include "aqhome/data/vars_dbread-t.h"
//#include "aqhome/data/vars_dbwrite-t.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>
#include <gwenhywfar/testframework.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 FULL_DEBUG
#define AQHOME_REACT_READNET_INTERVAL 300
#define AQHOME_REACT_SAVE_INTERVAL 300
#define CONNCLEAN_INTERVAL_IN_SECS 2
#define CONNCHECK_INTERVAL_IN_SECS 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop);
static int _diffInSeconds(time_t t1, time_t t0);
//static int _testModules(int argc, char **argv);
#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, saPIPE;
#endif
/* ------------------------------------------------------------------------------------------------
* static vars
* ------------------------------------------------------------------------------------------------
*/
static int stopService=0;
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
int rv;
AQH_EVENT_LOOP *eventLoop;
AQH_OBJECT *aqh;
GWEN_GUI *gui;
rv=GWEN_Init();
if (rv) {
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
return 2;
}
GWEN_Logger_Open(0, "aqhome-react", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
//GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info);
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);
eventLoop=AQH_EventLoop_new();
aqh=AQH_ReactServer_new(eventLoop);
rv=AQH_ReactServer_Init(aqh, argc, argv);
if (rv<0) {
if (rv==GWEN_ERROR_CLOSE)
return 1;
DBG_INFO(NULL, "here (%d)", rv);
return 2;
}
_runService(aqh, eventLoop);
AQH_ReactServer_Fini(aqh);
AQH_Object_free(aqh);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
{
time_t timeStart;
int timeout;
time_t timeLastConnCheck;
time_t lastFileScanTime;
time_t lastSaveTime;
timeout=AQH_ReactServer_GetTimeout(aqh);
timeStart=time(NULL);
timeLastConnCheck=time(NULL);
lastFileScanTime=timeStart;
lastSaveTime=timeStart;
AQH_ReactServer_SetLatestNetworkFileTime(aqh, AQH_ReactServer_GetNewestUnitNetFiletime());
while(!stopService) {
time_t now;
int rv;
AQH_EventLoop_Run(eventLoop, 2000);
AQH_ReactServer_HandleBrokerMsgs(aqh);
now=time(NULL);
if (_diffInSeconds(now, timeLastConnCheck)>CONNCHECK_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Check connections");
AQH_ReactServer_CheckBrokerConnection(aqh);
timeLastConnCheck=now;
}
AQH_ReactServer_ProcessAllUnits(aqh);
if (_diffInSeconds(now, lastFileScanTime)>AQHOME_REACT_READNET_INTERVAL) {
time_t tNew;
time_t t;
DBG_INFO(NULL, "Checking network files");
tNew=AQH_ReactServer_GetNewestUnitNetFiletime();
t=AQH_ReactServer_GetLatestNetworkFileTime(aqh);
if (tNew && tNew>t) {
DBG_INFO(NULL, "Reloading network files");
AQH_ReactServer_ReloadUnitNets(aqh);
AQH_ReactServer_SetLatestNetworkFileTime(aqh, tNew);
}
lastFileScanTime=now;
}
if (_diffInSeconds(now, lastSaveTime)>AQHOME_REACT_SAVE_INTERVAL) {
DBG_ERROR(NULL, "Writing var file");
rv=AQH_ReactServer_WriteVarsFile(aqh);
if (rv<0) {
DBG_INFO(NULL, "Error writing runtime data");
}
lastSaveTime=time(NULL);
}
if (timeout && (_diffInSeconds(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;
rv=_setupSigAction(&saPIPE, SIGPIPE);
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;
case SIGPIPE:
DBG_WARN(0, "Received PIPE signal");
break;
default:
DBG_WARN(0, "Unknown signal %d",s);
break;
}
}
#if 0
int _testModules(int argc, char **argv)
{
GWEN_GUI *gui;
GWEN_TEST_FRAMEWORK *tf;
int rv;
gui=GWEN_Gui_CGui_new();
GWEN_Gui_SetGui(gui);
tf=TestFramework_new();
rv=AQHREACT_PrgRule_AddTests(TestFramework_GetModulesRoot(tf));
if (rv<0) {
fprintf(stderr, "Adding module \"PrgRule\" failed.\n");
return 2;
}
rv=AQH_Path_AddTests(TestFramework_GetModulesRoot(tf));
if (rv<0) {
fprintf(stderr, "Adding module \"path\" failed.\n");
return 2;
}
rv=AQH_Vars_AddTests(TestFramework_GetModulesRoot(tf));
if (rv<0) {
fprintf(stderr, "Adding module \"vars\" failed.\n");
return 2;
}
rv=AQH_Vars_DbRead_AddTests(TestFramework_GetModulesRoot(tf));
if (rv<0) {
fprintf(stderr, "Adding module \"vars_dbread\" failed.\n");
return 2;
}
rv=AQH_Vars_DbWrite_AddTests(TestFramework_GetModulesRoot(tf));
if (rv<0) {
fprintf(stderr, "Adding module \"vars_dbwrite\" failed.\n");
return 2;
}
argc--;
argv++;
rv=TestFramework_Run(tf, argc, argv);
if (rv) {
fprintf(stderr, "SomeError in tests failed.\n");
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 2;
}
TestFramework_free(tf);
GWEN_Gui_SetGui(NULL);
GWEN_Gui_free(gui);
return 0;
}
#endif
int _diffInSeconds(time_t t1, time_t t0)
{
return t1-t0;
}

View File

@@ -0,0 +1,255 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 "./net_read.h"
#include "./server_p.h"
#include "aqhome-react/aqhome_react_p.h"
#include "aqhome-react/types/unit.h"
#include "aqhome-react/units/u_module.h"
#include "aqhome/aqhome.h"
#include <gwenhywfar/xml.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static time_t _getNewestFiletimeFromFileList(const GWEN_STRINGLIST *sl);
static AQHREACT_UNIT *_readNetworkFromFile(AQH_OBJECT *o, const char *filename);
static GWEN_XMLNODE *_readUnitNetFileToXml(const char *sFilename);
static int _readUnitNetFilesIntoList(AQH_OBJECT *o, const GWEN_STRINGLIST *sl, AQHREACT_UNIT_LIST *unitList);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
time_t AQH_ReactServer_GetNewestUnitNetFiletime(void)
{
GWEN_STRINGLIST *sl;
time_t t1;
time_t t2;
sl=AQH_GetListOfMatchingDataFiles("aqhome/react/networks", "*.xml");
t1=_getNewestFiletimeFromFileList(sl);
GWEN_StringList_free(sl);
sl=AQH_GetListOfMatchingSysconfFiles("aqhome/react/networks", "*.xml");
t2=_getNewestFiletimeFromFileList(sl);
GWEN_StringList_free(sl);
return (t1>t2)?t1:t2;
}
int AQH_ReactServer_ReadUnitNetFiles(AQH_OBJECT *aqh)
{
AQH_REACT_SERVER *xo;
xo=AQH_ReactServer_GetServerData(aqh);
if (xo) {
GWEN_STRINGLIST *sl;
sl=AQH_GetListOfMatchingSysconfFiles("aqhome/react/networks", "*.xml");
if (sl) {
int rv;
GWEN_StringList_Sort(sl, 1, GWEN_StringList_SortModeNoCase);
rv=_readUnitNetFilesIntoList(aqh, sl, xo->unitList);
GWEN_StringList_free(sl);
if (rv<0) {
DBG_INFO(NULL, "Error reading unit network files (%d)", rv);
return rv;
}
}
else {
DBG_ERROR(NULL, "No unit network files");
}
return 0;
}
return GWEN_ERROR_INVALID;
}
AQHREACT_UNIT *AQH_ReactServer_FindAndReadDataDirNetwork(AQH_OBJECT *o, const char *networkName)
{
if (o && networkName) {
AQH_REACT_SERVER *xo;
xo=AQH_ReactServer_GetServerData(o);
if (xo) {
AQHREACT_UNIT *unit;
GWEN_BUFFER *bufFilename;
GWEN_BUFFER *bufPath;
const char *s;
bufFilename=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendString(bufFilename, "aqhome/react/networks/");
s=networkName;
while(*s)
GWEN_Buffer_AppendByte(bufFilename, tolower((unsigned char) *(s++)));
GWEN_Buffer_AppendString(bufFilename, ".xml");
bufPath=AQH_FindPathOfDataFile(GWEN_Buffer_GetStart(bufFilename));
if (bufPath==NULL) {
DBG_ERROR(NULL, "Network file \"%s\" not found in data folders", GWEN_Buffer_GetStart(bufFilename));
GWEN_Buffer_free(bufFilename);
return NULL;
}
GWEN_Buffer_free(bufFilename);
unit=_readNetworkFromFile(o, GWEN_Buffer_GetStart(bufPath));
if (unit==NULL) {
DBG_ERROR(NULL, "Error reading network from file \"%s\"", GWEN_Buffer_GetStart(bufPath));
GWEN_Buffer_free(bufPath);
return NULL;
}
GWEN_Buffer_free(bufPath);
return unit;
}
}
return NULL;
}
time_t _getNewestFiletimeFromFileList(const GWEN_STRINGLIST *sl)
{
time_t resultTime=0;
if (sl) {
GWEN_STRINGLISTENTRY *se;
se=GWEN_StringList_FirstEntry(sl);
while(se) {
const char *s;
s=GWEN_StringListEntry_Data(se);
if (s && *s) {
struct stat sb;
if (stat(s, &sb)==0) {
time_t t;
t=sb.st_mtim.tv_sec;
if (t>resultTime)
resultTime=t;
}
else {
DBG_WARN(NULL, "Error on stat(%s): %s (%d)", s, strerror(errno), errno);
}
}
se=GWEN_StringListEntry_Next(se);
}
}
return resultTime;
}
AQHREACT_UNIT *_readNetworkFromFile(AQH_OBJECT *o, const char *filename)
{
GWEN_XMLNODE *n;
AQHREACT_UNIT *unit;
n=_readUnitNetFileToXml(filename);
if (n==NULL) {
DBG_ERROR(NULL, "Error reading network file \"%s\"", filename);
return NULL;
}
unit=AqHomeReact_UnitModule_fromXml(o, n);
if (unit==NULL) {
DBG_ERROR(NULL, "Error reading network from file \"%s\"", filename);
GWEN_XMLNode_free(n);
return NULL;
}
GWEN_XMLNode_free(n);
return unit;
}
GWEN_XMLNODE *_readUnitNetFileToXml(const char *sFilename)
{
GWEN_XMLNODE *rootNode;
GWEN_XMLNODE *netNode;
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 NULL;
}
netNode=GWEN_XMLNode_FindFirstTag(rootNode, "network", NULL, NULL);
if (netNode) {
GWEN_XMLNode_UnlinkChild(rootNode, netNode);
GWEN_XMLNode_free(rootNode);
return netNode;
}
else {
DBG_ERROR(NULL, "No \"network\" element in network file \"%s\"", sFilename);
return NULL;
}
}
int _readUnitNetFilesIntoList(AQH_OBJECT *o, const GWEN_STRINGLIST *sl, AQHREACT_UNIT_LIST *unitList)
{
GWEN_STRINGLISTENTRY *se;
se=GWEN_StringList_FirstEntry(sl);
while(se) {
const char *s;
s=GWEN_StringListEntry_Data(se);
if (s && *s) {
AQHREACT_UNIT *unit;
DBG_INFO(NULL, "Reading unit network file \"%s\"", s);
unit=_readNetworkFromFile(o, s);
if (unit==NULL) {
DBG_WARN(NULL, "No network read from network file \"%s\"", s);
return GWEN_ERROR_GENERIC;
}
AQHREACT_Unit_List_Add(unit, unitList);
}
se=GWEN_StringListEntry_Next(se);
}
return 0;
}

View File

@@ -0,0 +1,24 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 AQHOMEREACT_NET_READ_H
#define AQHOMEREACT_NET_READ_H
#include "./aqhome_react.h"
int AQH_ReactServer_ReadUnitNetFiles(AQH_OBJECT *aqh);
AQHREACT_UNIT *AQH_ReactServer_FindAndReadDataDirNetwork(AQH_OBJECT *aqh, const char *networkName);
time_t AQH_ReactServer_GetNewestUnitNetFiletime(void);
#endif

View File

@@ -0,0 +1,8 @@
<?xml?>
<gwbuild>
<data dist="true" install="$(datadir)/aqhome/react/networks">
delayedoff.xml
</data>
</gwbuild>

View File

@@ -0,0 +1,41 @@
<network type="delayedOff">
<paramdefs>
<param name="delayTime" targetModule="stabilize1" targetParam="holdTimeLow">30</param>
<param name="threshold" targetModule="highPass1" targetParam="threshold">70</param>
</paramdefs>
<inputPorts>
<inputPort name="input" dataType="double" />
</inputPorts>
<outputPorts>
<outputPort name="output" dataType="string" />
</outputPorts>
<units>
<unit id="highPass1" type="highPass" />
<unit id="stabilize1" type="stabilize">
<params>
<param name="holdTimeHigh">0.0</param>
</params>
</unit>
</units>
<links>
<!-- (sourceNet), sourceUnit, sourcePort, (targetNet), targetUnit, targetPort -->
<link sourceUnit=".timer" sourcePort="output" targetUnit="stabilize1" targetPort="timer" />
<link sourceUnit="." sourcePort="input" targetUnit="highPass1" targetPort="input" />
<link sourceUnit="highPass1" sourcePort="output" targetUnit="stabilize1" targetPort="input" />
<link sourceUnit="stabilize1" sourcePort="output" targetUnit="." targetPort="output" />
</links>
</network>

1207
apps/aqhome-react/server.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_REACT_SERVER_H
#define AQHOME_REACT_SERVER_H
#include <aqhome/events2/object.h>
#include "aqhome-react/types/unit.h"
#include "aqhome/data/vars.h"
AQH_OBJECT *AQH_ReactServer_new(AQH_EVENT_LOOP *eventLoop);
int AQH_ReactServer_Init(AQH_OBJECT *o, int argc, char **argv);
void AQH_ReactServer_Fini(AQH_OBJECT *o);
int AQH_ReactServer_GetTimeout(const AQH_OBJECT *o);
void AQH_ReactServer_SetVarsFile(AQH_OBJECT *o, const char *s);
time_t AQH_ReactServer_GetLatestNetworkFileTime(const AQH_OBJECT *aqh);
void AQH_ReactServer_SetLatestNetworkFileTime(AQH_OBJECT *aqh, time_t t);
AQHREACT_UNIT *AQH_ReactServer_GetTimerUnit(const AQH_OBJECT *aqh);
AQHREACT_UNIT *AQH_ReactServer_GetServerVarChangeUnit(const AQH_OBJECT *aqh);
AQH_OBJECT *AQH_ReactServer_GetBrokerEndpoint(const AQH_OBJECT *o);
void AQH_ReactServer_HandleBrokerMsgs(AQH_OBJECT *o);
void AQH_ReactServer_CheckBrokerConnection(AQH_OBJECT *o);
void AQH_ReactServer_ProcessAllUnits(AQH_OBJECT *o);
AQHREACT_UNIT *AQH_ReactServer_CreateUnitByName(AQH_OBJECT *aqh, const char *unitType);
int AQH_ReactServer_ReloadUnitNets(AQH_OBJECT *o);
AQHREACT_UNIT *AQH_ReactServer_FindUnitByUnitId(const AQH_OBJECT *aqh, const char *unitId);
void AQH_ReactServer_AddUnit(AQH_OBJECT *aqh, AQHREACT_UNIT *unit);
int AQH_ReactServer_SetCharValue(AQH_OBJECT *aqh, const char *path, const char *value);
const char *AQH_ReactServer_GetCharValue(AQH_OBJECT *aqh, const char *path, int idx, const char *defaultValue);
int AQH_ReactServer_SetDoubleValue(AQH_OBJECT *aqh, const char *path, double value);
double AQH_ReactServer_GetDoubleValue(AQH_OBJECT *aqh, const char *path, int idx, double defaultValue);
int AQH_ReactServer_SetIntValue(AQH_OBJECT *aqh, const char *path, int value);
int AQH_ReactServer_GetIntValue(AQH_OBJECT *aqh, const char *path, int idx, int defaultValue);
int AQH_ReactServer_IncIntValue(AQH_OBJECT *aqh, const char *path, int startValue, int defaultValue);
int AQH_ReactServer_DecIntValue(AQH_OBJECT *aqh, const char *path, int startValue, int defaultValue);
int AQH_ReactServer_WriteVarsFile(AQH_OBJECT *aqh);
#endif

View File

@@ -0,0 +1,64 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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_REACT_SERVER_P_H
#define AQHOME_REACT_SERVER_P_H
#include "./server.h"
#include <gwenhywfar/db.h>
/* default values */
#define AQHOME_REACT_DEFAULT_PIDFILE "/var/run/aqhome-react.pid"
#define AQHOME_REACT_DEFAULT_DATADIR "/var/lib/aqhome-react"
#define AQHOME_REACT_DEFAULT_VARSFILE "vars.conf"
#define AQHOME_REACT_DEFAULT_BROKER_PORT 1899
#define AQHOME_REACT_DEFAULT_BROKER_CLIENTID "react"
typedef struct AQH_REACT_SERVER AQH_REACT_SERVER;
struct AQH_REACT_SERVER {
AQH_OBJECT *brokerEndpoint;
GWEN_DB_NODE *dbArgs;
char *pidFile;
char *varsFile;
int timeout; /* timeout for run e.g. inside valgrind */
char *brokerAddress;
int brokerPort;
char *brokerClientId;
AQHREACT_UNIT *timerUnit;
AQHREACT_UNIT *serverVarChangeUnit;
AQHREACT_UNIT *localVarChangeUnit;
AQHREACT_UNIT_LIST *unitList;
AQH_VARS *localVars;
time_t latestNetworkFileTime;
time_t timestampBrokerDown;
int timeoutInSeconds;
};
AQH_REACT_SERVER *AQH_ReactServer_GetServerData(const AQH_OBJECT *o);
#endif

View File

@@ -0,0 +1,203 @@
/****************************************************************************
* 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 "./suntimes.h"
#include <stdio.h>
#include <math.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static GWEN_TIME *_getSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise);
static double _calcSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
GWEN_TIME *AQHomeReact_GetSunriseTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng)
{
return _getSunTime(date, lat, lng, 1);
}
GWEN_TIME *AQHomeReact_GetSunsetTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng)
{
return _getSunTime(date, lat, lng, 0);
}
GWEN_TIME *_getSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise)
{
double timeInFloat=fmod(24+_calcSunTime(date, lat, lng, calcSunrise), 24);
double hours;
double minutes = modf(timeInFloat, &hours)*60;
GWEN_TIME *ti=GWEN_Time_new(GWEN_Date_GetYear(date), GWEN_Date_GetMonth(date)-1, GWEN_Date_GetDay(date),
hours, minutes, 0, 1);
return ti;
}
#if 0
int _test(void) {
double lat = 52.619425; // Latitude of location.
double lng = 10.087891; // Longitude of location.
int y=2024;
int m=4;
int d;
for (d=19; d<=24; d++) {
GWEN_DATE *date;
GWEN_TIME *sunrise;
GWEN_TIME *sunset;
int sunsetHours, sunsetMinutes, sunsetSecs;
int sunriseHours, sunriseMinutes, sunriseSecs;
date=GWEN_Date_fromGregorian(y, m, d);
sunrise=AQHomeReact_GetSunriseTimeForDateAndLoc(date, lat, lng);
sunset=AQHomeReact_GetSunsetTimeForDateAndLoc(date, lat, lng);
GWEN_Time_GetBrokenDownTime(sunset, &sunsetHours, &sunsetMinutes, &sunsetSecs);
GWEN_Time_GetBrokenDownTime(sunrise, &sunriseHours, &sunriseMinutes, &sunriseSecs);
fprintf(stdout, "date=%04d-%02d-%02d: sunrise=%02d:%02d sunset=%02d:%02d\n",
GWEN_Date_GetYear(date), GWEN_Date_GetMonth(date), GWEN_Date_GetDay(date),
sunriseHours, sunriseMinutes,
sunsetHours, sunsetMinutes);
GWEN_Time_free(sunset);
GWEN_Time_free(sunrise);
GWEN_Date_free(date);
}
}
#endif
/**
* Calculate time of sunset or sunrise at a given position and date according to an algorithm presented
* on this site:
* http://edwilliams.org/sunrise_sunset_algorithm.htm
*
* Original notes follow.
*
* -----------------------------
* Source:
* Almanac for Computers, 1990
* published by Nautical Almanac Office
* United States Naval Observatory
* Washington, DC 20392
*
* Inputs:
* date : date for which to calculate sun time
* lat, lng: latitude and longitude of the location to calculate for (west and north are positive values)
* ZENITH : Sun's zenith for sunrise/sunset
* official = 90 degrees 50'
* civil = 96 degrees
* nautical = 102 degrees
* astronomical = 108 degrees
*
* NOTE: longitude is positive for East and negative for West
* NOTE: the algorithm assumes the use of a calculator with the
* trig functions in "degree" (rather than "radian") mode. Most
* programming languages assume radian arguments, requiring back
* and forth convertions. The factor is 180/pi. So, for instance,
* the equation RA = atan(0.91764 * tan(L)) would be coded as RA
* = (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree
* answer with a degree input for L.
*/
double _calcSunTime(const GWEN_DATE *date, double lat, double lng, int calcSunrise)
{
const double PI=3.1415926535897932384626433832795;
const double ZENITH=-.83; /* accounts for atmospheric refraction (assuming height of 0m above horizon) */
double PiBy180=PI/180.0;
double N;
double lngHour;
double t;
double M;
double L;
double RA;
double Lquadrant;
double RAquadrant;
double sinDec;
double cosDec;
double cosH;
double H;
double T;
double UT;
/* 1. first calculate the day of the year */
N=GWEN_Date_DaysInYear(date);
/* 2. convert the longitude to hour value and calculate an approximate time */
lngHour = lng/15.0;
if (calcSunrise)
t = N+((6-lngHour)/24);
else
t = N+((18-lngHour)/24);
/* 3. calculate the Sun's mean anomaly */
M = (0.9856*t)-3.289;
/* 4. calculate the Sun's true longitude */
L = fmod(M+(1.916*sin(PiBy180*M)) + (0.020 * sin(2*PiBy180*M)) + 282.634, 360.0);
/* 5a. calculate the Sun's right ascension */
RA = fmod(180/PI*atan(0.91764*tan(PiBy180*L)), 360.0);
/* 5b. right ascension value needs to be in the same quadrant as L */
Lquadrant = floor(L/90)*90;
RAquadrant = floor(RA/90)*90;
RA = RA + (Lquadrant-RAquadrant);
/* 5c. right ascension value needs to be converted into hours */
RA = RA / 15;
/* 6. calculate the Sun's declination */
sinDec = 0.39782*sin(PiBy180*L);
cosDec = cos(asin(sinDec));
/* 7a. calculate the Sun's local hour angle */
cosH = (sin(PiBy180*ZENITH)-(sinDec*sin(PiBy180*lat))) / (cosDec*cos(PiBy180*lat));
/* cosH >1 : sun never rises on this location and date
* cosH <-1: sun never sets on this location and date
*/
/* 7b. finish calculating H and convert into hours */
if (calcSunrise)
H = 360-(180/PI)*acos(cosH);
else
H = (180/PI)*acos(cosH);
H = H/15;
/* 8. calculate local mean time of rising/setting */
T = H+RA-(0.06571*t)-6.622;
/* 9. adjust back to UTC */
UT = fmod(T-lngHour, 24.0);
/* we don't do step 10. here which would be to convert UT into local time as we want UTC */
return UT;
}

View File

@@ -0,0 +1,47 @@
/****************************************************************************
* 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 AQHOMEREACT_SUNTIMES_H
#define AQHOMEREACT_SUNTIMES_H
#include <gwenhywfar/gwendate.h>
#include <gwenhywfar/gwentime.h>
/**
* Returns the sunrise time on the provided day and location.
*
* You can get the location by just clicking on a position on your chosen map website (e.g. "G'maps").
*
* @return time of sunrise for the given date and location
* @param date date for which to calculate
* @param lat latitude of the location to calculate for (positive for east)
* @param lng longitude of the location to calculate for (positive for north)
*/
GWEN_TIME *AQHomeReact_GetSunriseTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng);
/**
* Returns the sunset time on the provided day and location.
*
* You can get the location by just clicking on a position on your chosen map website (e.g. "G'maps").
*
* @return time of sunset for the given date and location
* @param date date for which to calculate
* @param lat latitude of the location to calculate for (positive for east)
* @param lng longitude of the location to calculate for (positive for north)
*/
GWEN_TIME *AQHomeReact_GetSunsetTimeForDateAndLoc(const GWEN_DATE *date, double lat, double lng);
#endif

View File

@@ -0,0 +1,85 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhreact_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" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="true" >
dataobject.h
dataobject_p.h
port.h
port_p.h
link.h
link_p.h
param.h
param_p.h
unit.h
unit_p.h
prgrule.h
prgrule_p.h
</headers>
<sources>
$(local/typefiles)
dataobject.c
port.c
link.c
param.c
unit.c
prgrule.c
</sources>
<useTargets>
</useTargets>
<libraries>
</libraries>
<subdirs>
</subdirs>
<extradist>
prgrule-t.h
prgrule-t.c
</extradist>
</target>
</gwbuild>

View File

@@ -0,0 +1,264 @@
/****************************************************************************
* 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 "./dataobject_p.h"
#include <gwenhywfar/list.h>
#include <gwenhywfar/debug.h>
GWEN_LIST_FUNCTIONS(AQHREACT_DATAOBJECT, AQHREACT_DataObject)
AQHREACT_DATAOBJECT *AQHREACT_DataObject_new()
{
AQHREACT_DATAOBJECT *dataObject;
GWEN_NEW_OBJECT(AQHREACT_DATAOBJECT, dataObject);
dataObject->_refCount=1;
GWEN_LIST_INIT(AQHREACT_DATAOBJECT, dataObject);
return dataObject;
}
void AQHREACT_DataObject_free(AQHREACT_DATAOBJECT *dataObject)
{
if (dataObject) {
if (dataObject->_refCount==1) {
GWEN_LIST_FINI(AQHREACT_DATAOBJECT, dataObject);
free(dataObject->systemValueId);
free(dataObject->valueUnits);
free(dataObject->stringData);
dataObject->_refCount=0;
GWEN_FREE_OBJECT(dataObject);
}
else if (dataObject->_refCount<1) {
DBG_ERROR(NULL, "dataobject already freed!");
}
else
dataObject->_refCount--;
}
}
AQHREACT_DATAOBJECT *AQHREACT_DataObject_dup(const AQHREACT_DATAOBJECT *origObject)
{
if (origObject) {
AQHREACT_DATAOBJECT *dataObject;
dataObject=AQHREACT_DataObject_new();
dataObject->valueId=origObject->valueId;
AQHREACT_DataObject_SetSystemValueId(dataObject, origObject->systemValueId);
AQHREACT_DataObject_SetValueUnits(dataObject, origObject->valueUnits);
dataObject->timestamp=origObject->timestamp;
dataObject->dataType=origObject->dataType;
dataObject->doubleData=origObject->doubleData;
dataObject->intData=origObject->intData;
AQHREACT_DataObject_SetStringData(dataObject, origObject->stringData);
return dataObject;
}
else
return NULL;
}
uint64_t AQHREACT_DataObject_GetValueId(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->valueId):0;
}
void AQHREACT_DataObject_SetValueId(AQHREACT_DATAOBJECT *dataObject, uint64_t i)
{
if (dataObject)
dataObject->valueId=i;
}
const char *AQHREACT_DataObject_GetSystemValueId(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->systemValueId):NULL;
}
void AQHREACT_DataObject_SetSystemValueId(AQHREACT_DATAOBJECT *dataObject, const char *s)
{
if (dataObject) {
free(dataObject->systemValueId);
dataObject->systemValueId=(s && *s)?strdup(s):NULL;
}
}
const char *AQHREACT_DataObject_GetValueUnits(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->valueUnits):NULL;
}
void AQHREACT_DataObject_SetValueUnits(AQHREACT_DATAOBJECT *dataObject, const char *s)
{
if (dataObject) {
free(dataObject->valueUnits);
dataObject->valueUnits=(s && *s)?strdup(s):NULL;
}
}
uint64_t AQHREACT_DataObject_GetTimestamp(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->timestamp):0;
}
void AQHREACT_DataObject_SetTimestamp(AQHREACT_DATAOBJECT *dataObject, uint64_t i)
{
if (dataObject)
dataObject->timestamp=i;
}
int AQHREACT_DataObject_GetDataType(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->dataType):AQHREACT_DATAOBJECTTYPE_UNKNOWN;
}
void AQHREACT_DataObject_SetDataType(AQHREACT_DATAOBJECT *dataObject, int i)
{
if (dataObject)
dataObject->dataType=i;
}
double AQHREACT_DataObject_GetDoubleData(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->doubleData):0.0;
}
void AQHREACT_DataObject_SetDoubleData(AQHREACT_DATAOBJECT *dataObject, double i)
{
if (dataObject)
dataObject->doubleData=i;
}
const char *AQHREACT_DataObject_GetStringData(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->stringData):NULL;
}
void AQHREACT_DataObject_SetStringData(AQHREACT_DATAOBJECT *dataObject, const char *s)
{
if (dataObject) {
free(dataObject->stringData);
dataObject->stringData=(s && *s)?strdup(s):NULL;
}
}
int AQHREACT_DataObject_GetIntData(const AQHREACT_DATAOBJECT *dataObject)
{
return dataObject?(dataObject->intData):0;
}
void AQHREACT_DataObject_SetIntData(AQHREACT_DATAOBJECT *dataObject, int i)
{
if (dataObject)
dataObject->intData=i;
}
const char *AQHREACT_DataObjectType_toString(int t)
{
switch(t) {
case AQHREACT_DATAOBJECTTYPE_DOUBLE: return "double";
case AQHREACT_DATAOBJECTTYPE_STRING: return "string";
case AQHREACT_DATAOBJECTTYPE_INT: return "int";
default: return "unknown";
}
}
int AQHREACT_DataObjectType_fromString(const char *s)
{
if (s && *s) {
if (strcasecmp(s, "double")==0)
return AQHREACT_DATAOBJECTTYPE_DOUBLE;
else if (strcasecmp(s, "string")==0)
return AQHREACT_DATAOBJECTTYPE_STRING;
else if (strcasecmp(s, "int")==0)
return AQHREACT_DATAOBJECTTYPE_INT;
}
return AQHREACT_DATAOBJECTTYPE_UNKNOWN;
}
void AQHREACT_DataObject_Dump(const AQHREACT_DATAOBJECT *dataObject, GWEN_BUFFER *buf, int indent)
{
if (dataObject && buf) {
const char *sVarSystemId;
sVarSystemId=AQHREACT_DataObject_GetSystemValueId(dataObject);
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
switch(dataObject->dataType) {
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
GWEN_Buffer_AppendArgs(buf, "%f (DOUBLE) [%s]\n", AQHREACT_DataObject_GetDoubleData(dataObject), sVarSystemId?sVarSystemId:"");
break;
case AQHREACT_DATAOBJECTTYPE_STRING:
GWEN_Buffer_AppendArgs(buf, "%s (STRING) [%s]\n", AQHREACT_DataObject_GetStringData(dataObject), sVarSystemId?sVarSystemId:"");
break;
case AQHREACT_DATAOBJECTTYPE_INT:
GWEN_Buffer_AppendArgs(buf, "%d (INT) [%s]\n", AQHREACT_DataObject_GetIntData(dataObject), sVarSystemId?sVarSystemId:"");
break;
default:
break;
}
}
}

View File

@@ -0,0 +1,70 @@
/****************************************************************************
* 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_REACT_DATAOBJECT_H
#define AQHOME_REACT_DATAOBJECT_H
#include <gwenhywfar/list.h>
typedef struct AQHREACT_DATAOBJECT AQHREACT_DATAOBJECT;
GWEN_LIST_FUNCTION_DEFS(AQHREACT_DATAOBJECT, AQHREACT_DataObject)
enum {
AQHREACT_DATAOBJECTTYPE_UNKNOWN=0,
AQHREACT_DATAOBJECTTYPE_DOUBLE,
AQHREACT_DATAOBJECTTYPE_STRING,
AQHREACT_DATAOBJECTTYPE_INT
};
#include "aqhome-react/aqhome_react.h"
AQHREACT_DATAOBJECT *AQHREACT_DataObject_new();
void AQHREACT_DataObject_free(AQHREACT_DATAOBJECT *dataObject);
AQHREACT_DATAOBJECT *AQHREACT_DataObject_dup(const AQHREACT_DATAOBJECT *origObject);
uint64_t AQHREACT_DataObject_GetValueId(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetValueId(AQHREACT_DATAOBJECT *dataObject, uint64_t i);
const char *AQHREACT_DataObject_GetSystemValueId(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetSystemValueId(AQHREACT_DATAOBJECT *dataObject, const char *s);
const char *AQHREACT_DataObject_GetValueUnits(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetValueUnits(AQHREACT_DATAOBJECT *dataObject, const char *s);
uint64_t AQHREACT_DataObject_GetTimestamp(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetTimestamp(AQHREACT_DATAOBJECT *dataObject, uint64_t i);
int AQHREACT_DataObject_GetDataType(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetDataType(AQHREACT_DATAOBJECT *dataObject, int i);
double AQHREACT_DataObject_GetDoubleData(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetDoubleData(AQHREACT_DATAOBJECT *dataObject, double i);
const char *AQHREACT_DataObject_GetStringData(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetStringData(AQHREACT_DATAOBJECT *dataObject, const char *s);
int AQHREACT_DataObject_GetIntData(const AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_DataObject_SetIntData(AQHREACT_DATAOBJECT *dataObject, int i);
void AQHREACT_DataObject_Dump(const AQHREACT_DATAOBJECT *dataObject, GWEN_BUFFER *buf, int indent);
const char *AQHREACT_DataObjectType_toString(int t);
int AQHREACT_DataObjectType_fromString(const char *s);
#endif

View File

@@ -0,0 +1,32 @@
/****************************************************************************
* 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_REACT_DATAOBJECT_P_H
#define AQHOME_REACT_DATAOBJECT_P_H
#include "aqhome-react/types/dataobject.h"
struct AQHREACT_DATAOBJECT {
GWEN_LIST_ELEMENT(AQHREACT_DATAOBJECT)
int _refCount;
uint64_t valueId;
char *systemValueId;
char *valueUnits;
uint64_t timestamp;
int dataType;
double doubleData;
int intData;
char *stringData;
};
#endif

View File

@@ -0,0 +1,117 @@
/****************************************************************************
* 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 "./link_p.h"
#include <gwenhywfar/debug.h>
GWEN_LIST_FUNCTIONS(AQHREACT_LINK, AQHREACT_Link)
AQHREACT_LINK *AQHREACT_Link_new()
{
AQHREACT_LINK *lnk;
GWEN_NEW_OBJECT(AQHREACT_LINK, lnk);
GWEN_LIST_INIT(AQHREACT_LINK, lnk);
return lnk;
}
void AQHREACT_Link_free(AQHREACT_LINK *lnk)
{
if (lnk) {
GWEN_LIST_FINI(AQHREACT_LINK, lnk);
GWEN_FREE_OBJECT(lnk);
}
}
AQHREACT_UNIT *AQHREACT_Link_GetTargetUnit(const AQHREACT_LINK *lnk)
{
return lnk?lnk->targetUnit:NULL;
}
void AQHREACT_Link_SetTargetUnit(AQHREACT_LINK *lnk, AQHREACT_UNIT *unit)
{
if (lnk)
lnk->targetUnit=unit;
}
AQHREACT_PORT *AQHREACT_Link_GetTargetPort(const AQHREACT_LINK *lnk)
{
return lnk?lnk->targetPort:NULL;
}
void AQHREACT_Link_SetTargetPort(AQHREACT_LINK *lnk, AQHREACT_PORT *port)
{
if (lnk)
lnk->targetPort=port;
}
void AQHREACT_Link_Dump(const AQHREACT_LINK *lnk, GWEN_BUFFER *buf, int indent)
{
if (lnk && buf) {
const char *unitName;
const char *unitId;
const char *portName;
unitName=(lnk->targetUnit)?AQHREACT_Unit_GetTypeName(lnk->targetUnit):NULL;
unitId=(lnk->targetUnit)?AQHREACT_Unit_GetId(lnk->targetUnit):NULL;
portName=(lnk->targetPort)?AQHREACT_Port_GetName(lnk->targetPort):NULL;
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
GWEN_Buffer_AppendArgs(buf, "- target unit id: %s (%s), target slot name: %s [%d]\n",
unitId?unitId:"<no id>",
unitName?unitName:"<no name>",
portName?portName:"<no port>",
(lnk->targetPort)?AQHREACT_Port_GetIdForUnit(lnk->targetPort):0);
}
}
void AQHREACT_Link_List_Dump(const AQHREACT_LINK_LIST *lnkList, GWEN_BUFFER *buf, int indent, const char *title)
{
if (lnkList && buf) {
const AQHREACT_LINK *lnk;
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Link List:\n");
lnk=AQHREACT_Link_List_First(lnkList);
while(lnk) {
AQHREACT_Link_Dump(lnk, buf, indent+2);
lnk=AQHREACT_Link_List_Next(lnk);
}
}
}

View File

@@ -0,0 +1,39 @@
/****************************************************************************
* 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_REACT_LINK_H
#define AQHOME_REACT_LINK_H
#include <gwenhywfar/list.h>
typedef struct AQHREACT_LINK AQHREACT_LINK;
GWEN_LIST_FUNCTION_DEFS(AQHREACT_LINK, AQHREACT_Link)
#include "aqhome-react/types/unit.h"
#include "aqhome-react/types/port.h"
AQHREACT_LINK *AQHREACT_Link_new();
void AQHREACT_Link_free(AQHREACT_LINK *lnk);
AQHREACT_UNIT *AQHREACT_Link_GetTargetUnit(const AQHREACT_LINK *lnk);
void AQHREACT_Link_SetTargetUnit(AQHREACT_LINK *lnk, AQHREACT_UNIT *unit);
AQHREACT_PORT *AQHREACT_Link_GetTargetPort(const AQHREACT_LINK *lnk);
void AQHREACT_Link_SetTargetPort(AQHREACT_LINK *lnk, AQHREACT_PORT *port);
void AQHREACT_Link_Dump(const AQHREACT_LINK *lnk, GWEN_BUFFER *buf, int indent);
void AQHREACT_Link_List_Dump(const AQHREACT_LINK_LIST *lnkList, GWEN_BUFFER *buf, int indent, const char *title);
#endif

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_REACT_LINK_P_H
#define AQHOME_REACT_LINK_P_H
#include "aqhome-react/types/link.h"
struct AQHREACT_LINK {
GWEN_LIST_ELEMENT(AQHREACT_LINK)
AQHREACT_UNIT *targetUnit;
AQHREACT_PORT *targetPort;
};
#endif

View File

@@ -0,0 +1,294 @@
/****************************************************************************
* 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 "./param_p.h"
#include "./dataobject.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
GWEN_LIST_FUNCTIONS(AQHREACT_PARAM, AQHREACT_Param)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _setDbValueFromParamString(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags);
static void _setDbValueFromParamDouble(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHREACT_PARAM *AQHREACT_Param_new()
{
AQHREACT_PARAM *param;
GWEN_NEW_OBJECT(AQHREACT_PARAM, param);
GWEN_LIST_INIT(AQHREACT_PARAM, param);
return param;
}
void AQHREACT_Param_free(AQHREACT_PARAM *param)
{
if (param) {
GWEN_LIST_FINI(AQHREACT_PARAM, param);
free(param->name);
free(param->description);
free(param->stringValue);
GWEN_FREE_OBJECT(param);
}
}
const char *AQHREACT_Param_GetName(const AQHREACT_PARAM *param)
{
return param?param->name:NULL;
}
void AQHREACT_Param_SetName(AQHREACT_PARAM *param, const char *s)
{
if (param) {
free(param->name);
param->name=s?strdup(s):NULL;
}
}
const char *AQHREACT_Param_GetDescription(const AQHREACT_PARAM *param)
{
return param?param->description:NULL;
}
void AQHREACT_Param_SetDescription(AQHREACT_PARAM *param, const char *s)
{
if (param) {
free(param->description);
param->description=s?strdup(s):NULL;
}
}
int AQHREACT_Param_GetDataType(const AQHREACT_PARAM *param)
{
return param?param->dataType:AQHREACT_DATAOBJECTTYPE_UNKNOWN;
}
void AQHREACT_Param_SetDataType(AQHREACT_PARAM *param, int i)
{
if (param)
param->dataType=i;
}
const char *AQHREACT_Param_GetStringValue(const AQHREACT_PARAM *param)
{
return param?param->stringValue:NULL;
}
void AQHREACT_Param_SetStringValue(AQHREACT_PARAM *param, const char *s)
{
if (param) {
free(param->stringValue);
param->stringValue=s?strdup(s):NULL;
}
}
double AQHREACT_Param_GetDoubleValue(const AQHREACT_PARAM *param)
{
return param?param->doubleValue:0.0;
}
void AQHREACT_Param_SetDoubleValue(AQHREACT_PARAM *param, double d)
{
if (param)
param->doubleValue=d;
}
uint32_t AQHREACT_Param_GetFlags(const AQHREACT_PARAM *param)
{
return param?param->flags:0;
}
void AQHREACT_Param_SetFlags(AQHREACT_PARAM *param, uint32_t f)
{
if (param)
param->flags=f;
}
void AQHREACT_Param_AddFlags(AQHREACT_PARAM *param, uint32_t f)
{
if (param)
param->flags|=f;
}
void AQHREACT_Param_SubFlags(AQHREACT_PARAM *param, uint32_t f)
{
if (param)
param->flags&=~f;
}
AQHREACT_PARAM *AQHREACT_Param_List_GetParamByName(const AQHREACT_PARAM_LIST *paramList, const char *paramName)
{
if (paramList && paramName && *paramName) {
AQHREACT_PARAM *param;
param=AQHREACT_Param_List_First(paramList);
while(param) {
const char *s;
s=AQHREACT_Param_GetName(param);
if (s && *s && strcasecmp(paramName, s)==0)
return param;
param=AQHREACT_Param_List_Next(param);
}
}
return NULL;
}
int AQHREACT_Param_List_WriteIntoDb(const AQHREACT_PARAM_LIST *paramList, GWEN_DB_NODE *db, uint32_t dbFlags)
{
if (paramList && db) {
const AQHREACT_PARAM *param;
param=AQHREACT_Param_List_First(paramList);
while(param) {
if (param->name) {
switch(param->dataType) {
case AQHREACT_DATAOBJECTTYPE_STRING:
_setDbValueFromParamString(param, db, dbFlags);
break;
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
_setDbValueFromParamDouble(param, db, dbFlags);
break;
default:
DBG_ERROR(NULL, "Unhandled param type %d", param->dataType);
return GWEN_ERROR_GENERIC;
}
}
param=AQHREACT_Param_List_Next(param);
}
}
return 0;
}
void _setDbValueFromParamString(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags)
{
if (param->stringValue)
GWEN_DB_SetCharValue(db, dbFlags, param->name, param->stringValue);
else
GWEN_DB_DeleteVar(db, param->name);
}
void _setDbValueFromParamDouble(const AQHREACT_PARAM *param, GWEN_DB_NODE *db, uint32_t dbFlags)
{
GWEN_BUFFER *buf;
int rv;
buf=GWEN_Buffer_new(0, 32, 0, 1);
rv=GWEN_Text_DoubleToBuffer(param->doubleValue, buf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
else
GWEN_DB_SetCharValue(db, dbFlags, param->name, GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
}
void AQHREACT_Param_Dump(const AQHREACT_PARAM *param, GWEN_BUFFER *buf, int indent)
{
if (param && buf) {
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
switch(param->dataType) {
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
GWEN_Buffer_AppendArgs(buf, "- %s = %f [DOUBLE]\n", (param->name)?(param->name):"<unnamed>", param->doubleValue);
break;
case AQHREACT_DATAOBJECTTYPE_STRING:
GWEN_Buffer_AppendArgs(buf, "- %s = %s [STRING]\n", (param->name)?(param->name):"<unnamed>", param->stringValue);
break;
default:
DBG_ERROR(NULL, "Unhandled data type %d", param->dataType);
}
}
}
void AQHREACT_Param_List_Dump(const AQHREACT_PARAM_LIST *paramList, GWEN_BUFFER *buf, int indent, const char *title)
{
if (paramList && buf) {
const AQHREACT_PARAM *param;
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Parameter List:\n");
param=AQHREACT_Param_List_First(paramList);
while(param) {
AQHREACT_Param_Dump(param, buf, indent+2);
param=AQHREACT_Param_List_Next(param);
}
}
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
* 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_REACT_PARAM_H
#define AQHOME_REACT_PARAM_H
#include <gwenhywfar/list.h>
#include <gwenhywfar/db.h>
typedef struct AQHREACT_PARAM AQHREACT_PARAM;
GWEN_LIST_FUNCTION_DEFS(AQHREACT_PARAM, AQHREACT_Param)
#include "aqhome-react/aqhome_react.h"
AQHREACT_PARAM *AQHREACT_Param_new();
void AQHREACT_Param_free(AQHREACT_PARAM *param);
const char *AQHREACT_Param_GetName(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetName(AQHREACT_PARAM *param, const char *s);
const char *AQHREACT_Param_GetDescription(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetDescription(AQHREACT_PARAM *param, const char *s);
int AQHREACT_Param_GetDataType(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetDataType(AQHREACT_PARAM *param, int i);
const char *AQHREACT_Param_GetStringValue(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetStringValue(AQHREACT_PARAM *param, const char *s);
double AQHREACT_Param_GetDoubleValue(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetDoubleValue(AQHREACT_PARAM *param, double d);
uint32_t AQHREACT_Param_GetFlags(const AQHREACT_PARAM *param);
void AQHREACT_Param_SetFlags(AQHREACT_PARAM *param, uint32_t f);
void AQHREACT_Param_AddFlags(AQHREACT_PARAM *param, uint32_t f);
void AQHREACT_Param_SubFlags(AQHREACT_PARAM *param, uint32_t f);
AQHREACT_PARAM *AQHREACT_Param_List_GetParamByName(const AQHREACT_PARAM_LIST *paramList, const char *paramName);
int AQHREACT_Param_List_WriteIntoDb(const AQHREACT_PARAM_LIST *paramList, GWEN_DB_NODE *db, uint32_t dbFlags);
void AQHREACT_Param_Dump(const AQHREACT_PARAM *param, GWEN_BUFFER *buf, int indent);
void AQHREACT_Param_List_Dump(const AQHREACT_PARAM_LIST *paramList, GWEN_BUFFER *buf, int indent, const char *title);
#endif

View File

@@ -0,0 +1,30 @@
/****************************************************************************
* 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_REACT_PARAM_P_H
#define AQHOME_REACT_PARAM_P_H
#include "aqhome-react/types/param.h"
struct AQHREACT_PARAM {
GWEN_LIST_ELEMENT(AQHREACT_PARAM)
char *name;
char *description;
int dataType;
char *stringValue;
double doubleValue;
uint32_t flags;
};
#endif

View File

@@ -0,0 +1,303 @@
/****************************************************************************
* 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 "./port_p.h"
#include "aqhome-react/types/dataobject.h"
#include <gwenhywfar/debug.h>
GWEN_LIST_FUNCTIONS(AQHREACT_PORT, AQHREACT_Port)
AQHREACT_PORT *AQHREACT_Port_new()
{
AQHREACT_PORT *port;
GWEN_NEW_OBJECT(AQHREACT_PORT, port);
GWEN_LIST_INIT(AQHREACT_PORT, port);
port->linkList=AQHREACT_Link_List_new();
return port;
}
AQHREACT_PORT *AQHREACT_Port_dup(const AQHREACT_PORT *srcPort)
{
if (srcPort) {
AQHREACT_PORT *newPort;
newPort=AQHREACT_Port_new();
AQHREACT_Port_SetName(newPort, srcPort->name);
newPort->idForUnit=srcPort->idForUnit;
AQHREACT_Port_SetDescription(newPort, srcPort->description);
newPort->flags=srcPort->flags;
newPort->dataType=srcPort->dataType;
/* don't copy current dataObject */
/* don't copy current links */
return newPort;
}
return NULL;
}
void AQHREACT_Port_free(AQHREACT_PORT *port)
{
if (port) {
GWEN_LIST_FINI(AQHREACT_PORT, port);
AQHREACT_DataObject_free(port->currentDataObject);
AQHREACT_Link_List_free(port->linkList);
free(port->name);
free(port->description);
GWEN_FREE_OBJECT(port);
}
}
const char *AQHREACT_Port_GetName(const AQHREACT_PORT *port)
{
return port?port->name:NULL;
}
void AQHREACT_Port_SetName(AQHREACT_PORT *port, const char *s)
{
if (port) {
free(port->name);
port->name=s?strdup(s):NULL;
}
}
int AQHREACT_Port_GetIdForUnit(const AQHREACT_PORT *port)
{
return port?port->idForUnit:0;
}
void AQHREACT_Port_SetIdForUnit(AQHREACT_PORT *port, int i)
{
if (port)
port->idForUnit=i;
}
const char *AQHREACT_Port_GetDescription(const AQHREACT_PORT *port)
{
return port?port->description:NULL;
}
void AQHREACT_Port_SetDescription(AQHREACT_PORT *port, const char *s)
{
if (port) {
free(port->description);
port->description=s?strdup(s):NULL;
}
}
uint32_t AQHREACT_Port_GetFlags(const AQHREACT_PORT *port)
{
return port?port->flags:0;
}
void AQHREACT_Port_SetFlags(AQHREACT_PORT *port, uint32_t f)
{
if (port)
port->flags=f;
}
void AQHREACT_Port_AddFlags(AQHREACT_PORT *port, uint32_t f)
{
if (port)
port->flags|=f;
}
void AQHREACT_Port_SubFlags(AQHREACT_PORT *port, uint32_t f)
{
if (port)
port->flags&=~f;
}
int AQHREACT_Port_GetDataType(const AQHREACT_PORT *port)
{
return port?port->dataType:AQHREACT_DATAOBJECTTYPE_UNKNOWN;
}
void AQHREACT_Port_SetDataType(AQHREACT_PORT *port, int i)
{
if (port)
port->dataType=i;
}
AQHREACT_DATAOBJECT *AQHREACT_Port_GetCurrentDataObject(const AQHREACT_PORT *port)
{
return port?port->currentDataObject:NULL;
}
void AQHREACT_Port_SetCurrentDataObject(AQHREACT_PORT *port, AQHREACT_DATAOBJECT *dataObject)
{
if (port){
AQHREACT_DataObject_free(port->currentDataObject);
port->currentDataObject=dataObject;
}
}
AQHREACT_LINK_LIST *AQHREACT_Port_GetLinkList(const AQHREACT_PORT *port)
{
return port?port->linkList:NULL;
}
void AQHREACT_Port_AddLink(AQHREACT_PORT *port, AQHREACT_LINK *lnk)
{
if (port)
AQHREACT_Link_List_Add(lnk, port->linkList);
}
void AQHREACT_Port_SendData(const AQHREACT_PORT *port, const AQHREACT_DATAOBJECT *dataObject)
{
AQHREACT_LINK_LIST *linkList;
linkList=AQHREACT_Port_GetLinkList(port);
if (linkList) {
AQHREACT_LINK *lnk;
lnk=AQHREACT_Link_List_First(linkList);
while(lnk) {
AQHREACT_UNIT *targetUnit;
AQHREACT_PORT *targetPort;
targetUnit=AQHREACT_Link_GetTargetUnit(lnk);
targetPort=AQHREACT_Link_GetTargetPort(lnk);
if (targetUnit && targetPort) {
DBG_DEBUG(NULL, "%s: Sending data to %s:%s",
AQHREACT_Port_GetName(port), AQHREACT_Unit_GetId(targetUnit), AQHREACT_Port_GetName(targetPort));
AQHREACT_Unit_InputData(targetUnit, targetPort, dataObject);
}
lnk=AQHREACT_Link_List_Next(lnk);
} /* while */
} /* if linkList */
}
AQHREACT_PORT *AQHREACT_Port_List_GetByName(const AQHREACT_PORT_LIST *portList, const char *name)
{
if (portList && name) {
AQHREACT_PORT *port;
port=AQHREACT_Port_List_First(portList);
while(port) {
const char *s;
s=AQHREACT_Port_GetName(port);
if (s && strcasecmp(s, name)==0)
return port;
port=AQHREACT_Port_List_Next(port);
}
}
return NULL;
}
AQHREACT_PORT *AQHREACT_Port_List_GetByIdForUnit(const AQHREACT_PORT_LIST *portList, int id)
{
if (portList) {
AQHREACT_PORT *port;
port=AQHREACT_Port_List_First(portList);
while(port) {
if (id==AQHREACT_Port_GetIdForUnit(port))
return port;
port=AQHREACT_Port_List_Next(port);
}
}
return NULL;
}
void AQHREACT_Port_Dump(const AQHREACT_PORT *port, GWEN_BUFFER *buf, int indent)
{
if (port && buf) {
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
GWEN_Buffer_AppendArgs(buf, "- \"%s\"[%d] (%s, %08x)\n",
(port->name)?(port->name):"<no name>",
port->idForUnit,
AQHREACT_DataObjectType_toString(port->dataType),
port->flags);
if (port->linkList && AQHREACT_Link_List_GetCount(port->linkList))
AQHREACT_Link_List_Dump(port->linkList, buf, indent+2, "Link List:");
}
}
void AQHREACT_Port_List_Dump(const AQHREACT_PORT_LIST *portList, GWEN_BUFFER *buf, int indent, const char *title)
{
if (portList && buf) {
const AQHREACT_PORT *port;
if (indent)
GWEN_Buffer_FillWithBytes(buf, ' ', indent);
GWEN_Buffer_AppendArgs(buf, "%s\n", (title && *title)?title:"Port List:\n");
port=AQHREACT_Port_List_First(portList);
while(port) {
AQHREACT_Port_Dump(port, buf, indent+2);
port=AQHREACT_Port_List_Next(port);
}
}
}

View File

@@ -0,0 +1,67 @@
/****************************************************************************
* 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_REACT_PORT_H
#define AQHOME_REACT_PORT_H
#include <gwenhywfar/list.h>
typedef struct AQHREACT_PORT AQHREACT_PORT;
GWEN_LIST_FUNCTION_DEFS(AQHREACT_PORT, AQHREACT_Port)
#include "aqhome-react/aqhome_react.h"
#include "aqhome-react/types/link.h"
#include "aqhome-react/types/dataobject.h"
AQHREACT_PORT *AQHREACT_Port_new();
AQHREACT_PORT *AQHREACT_Port_dup(const AQHREACT_PORT *srcPort);
void AQHREACT_Port_free(AQHREACT_PORT *port);
const char *AQHREACT_Port_GetName(const AQHREACT_PORT *port);
void AQHREACT_Port_SetName(AQHREACT_PORT *port, const char *s);
int AQHREACT_Port_GetIdForUnit(const AQHREACT_PORT *port);
void AQHREACT_Port_SetIdForUnit(AQHREACT_PORT *port, int i);
const char *AQHREACT_Port_GetDescription(const AQHREACT_PORT *port);
void AQHREACT_Port_SetDescription(AQHREACT_PORT *port, const char *s);
uint32_t AQHREACT_Port_GetFlags(const AQHREACT_PORT *port);
void AQHREACT_Port_SetFlags(AQHREACT_PORT *port, uint32_t f);
void AQHREACT_Port_AddFlags(AQHREACT_PORT *port, uint32_t f);
void AQHREACT_Port_SubFlags(AQHREACT_PORT *port, uint32_t f);
int AQHREACT_Port_GetDataType(const AQHREACT_PORT *port);
void AQHREACT_Port_SetDataType(AQHREACT_PORT *port, int i);
AQHREACT_DATAOBJECT *AQHREACT_Port_GetCurrentDataObject(const AQHREACT_PORT *port);
void AQHREACT_Port_SetCurrentDataObject(AQHREACT_PORT *port, AQHREACT_DATAOBJECT *dataObject);
void AQHREACT_Port_SendData(const AQHREACT_PORT *port, const AQHREACT_DATAOBJECT *dataObject);
AQHREACT_LINK_LIST *AQHREACT_Port_GetLinkList(const AQHREACT_PORT *port);
void AQHREACT_Port_AddLink(AQHREACT_PORT *port, AQHREACT_LINK *lnk);
AQHREACT_PORT *AQHREACT_Port_List_GetByName(const AQHREACT_PORT_LIST *portList, const char *name);
AQHREACT_PORT *AQHREACT_Port_List_GetByIdForUnit(const AQHREACT_PORT_LIST *portList, int id);
void AQHREACT_Port_Dump(const AQHREACT_PORT *port, GWEN_BUFFER *buf, int indent);
void AQHREACT_Port_List_Dump(const AQHREACT_PORT_LIST *portList, GWEN_BUFFER *buf, int indent, const char *title);
#endif

View File

@@ -0,0 +1,31 @@
/****************************************************************************
* 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_REACT_PORT_P_H
#define AQHOME_REACT_PORT_P_H
#include "aqhome-react/types/port.h"
struct AQHREACT_PORT {
GWEN_LIST_ELEMENT(AQHREACT_PORT)
char *name;
char *description;
int idForUnit;
uint32_t flags;
int dataType;
AQHREACT_DATAOBJECT *currentDataObject;
AQHREACT_LINK_LIST *linkList;
};
#endif

View File

@@ -0,0 +1,182 @@
/****************************************************************************
* 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.
****************************************************************************/
/* This file is included by "prgrule.c" */
#include <gwenhywfar/testframework.h>
#include "prgrule-t.h"
#ifdef AQHOME_ENABLE_TESTCODE
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int GWENHYWFAR_CB test1(GWEN_TEST_MODULE *mod);
static int GWENHYWFAR_CB test2(GWEN_TEST_MODULE *mod);
static int GWENHYWFAR_CB test3(GWEN_TEST_MODULE *mod);
static int testAgainstValues(const char *ruleText, const uint64_t *ptrExpectedFields, double expectedValue);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod)
{
GWEN_TEST_MODULE *newMod;
newMod=GWEN_Test_Module_AddModule(mod, "AQHREACT_PrgRule", NULL);
GWEN_Test_Module_AddTest(newMod, "parse rules 1", test1, NULL);
GWEN_Test_Module_AddTest(newMod, "parse rules 2", test2, NULL);
GWEN_Test_Module_AddTest(newMod, "match rules 1", test3, NULL);
return 0;
}
int test1(GWEN_UNUSED GWEN_TEST_MODULE *mod)
{
static char ruleText[]="* * * * * 10.0";
uint64_t expectedFields[]={
0b111111111111111111111111111111111111111111111111111111111111L, /* minute */
0b111111111111111111111111L, /* hour */
0b11111111111111111111111111111110L, /* day of month */
0b1111111111110L, /* month */
0b1111111L /* day of week (0=sunday) */
};
return testAgainstValues(ruleText, expectedFields, 10.0);
}
int test2(GWEN_UNUSED GWEN_TEST_MODULE *mod)
{
static char ruleText[]="/2 4-7 1-5,8,15,23-25 * 0,3,5 12.3";
uint64_t expectedFields[]={
0b010101010101010101010101010101010101010101010101010101010101L, /* minute */
0b000000000000000011110000L, /* hour */
0b00000011100000001000000100111110L, /* day of month */
0b1111111111110L, /* month */
0b0101001L /* day of week (0=sunday) */
};
return testAgainstValues(ruleText, expectedFields, 12.3);
}
int test3(GWEN_UNUSED GWEN_TEST_MODULE *mod)
{
AQHREACT_PRGRULE_LIST *prgRuleList;
AQHREACT_PRGRULE *prgRule;
static char ruleText[]="/2 4-7 1-5,8,15,23-25 * 0,1,3,5 12.3";
prgRuleList=AQHREACT_PrgRule_ReadRules(ruleText);
if (prgRuleList==NULL) {
DBG_ERROR(NULL, "Error reading rules list");
return GWEN_ERROR_GENERIC;
}
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
if (!AQHREACT_PrgRule_Matches(prgRule, 2, 5, 8, 4, 1)) {
DBG_ERROR(NULL, "Error matching against rule");
return GWEN_ERROR_GENERIC;
}
return 0;
}
int testAgainstValues(const char *ruleText, const uint64_t *ptrExpectedFields, double expectedValue)
{
AQHREACT_PRGRULE_LIST *prgRuleList;
AQHREACT_PRGRULE *prgRule;
prgRuleList=AQHREACT_PrgRule_ReadRules(ruleText);
if (prgRuleList==NULL) {
DBG_ERROR(NULL, "Error reading rules list");
return GWEN_ERROR_GENERIC;
}
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
if (AQHREACT_PrgRule_List_Next(prgRule)) {
DBG_ERROR(NULL, "More than one rule read when only one given");
return GWEN_ERROR_GENERIC;
}
if (prgRule->bitfieldMinute!=ptrExpectedFields[0]) {
DBG_ERROR(NULL, "Unexpected value for MINUTE: %016lX [%016lX]", prgRule->bitfieldMinute, ptrExpectedFields[0]);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
if (prgRule->bitfieldHour!=ptrExpectedFields[1]) {
DBG_ERROR(NULL, "Unexpected value for HOUR: %016X [%016lX]", prgRule->bitfieldHour, ptrExpectedFields[1]);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
if (prgRule->bitfieldDayOfMonth!=ptrExpectedFields[2]) {
DBG_ERROR(NULL, "Unexpected value for DAYOFMONTH: %016X [%016lX]", prgRule->bitfieldDayOfMonth, ptrExpectedFields[2]);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
if (prgRule->bitfieldMonth!=ptrExpectedFields[3]) {
DBG_ERROR(NULL, "Unexpected value for MONTH: %016X [%016lX]", prgRule->bitfieldMonth, ptrExpectedFields[3]);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
if (prgRule->bitfieldDayOfWeek!=ptrExpectedFields[4]) {
DBG_ERROR(NULL, "Unexpected value for DAYOFWEEK: %08X [%016lX]", prgRule->bitfieldDayOfWeek, ptrExpectedFields[4]);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
if (prgRule->value!=expectedValue) {
DBG_ERROR(NULL, "Unexpected value : %f", prgRule->value);
AQHREACT_PrgRule_List_free(prgRuleList);
return GWEN_ERROR_GENERIC;
}
return 0;
}
#else
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod)
{
DBG_ERROR(GWEN_LOGDOMAIN, "AqHome was compiled without test code enabled.");
return GWEN_ERROR_GENERIC;
}
#endif

View File

@@ -0,0 +1,26 @@
/****************************************************************************
* 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_REACT_PRGRULE_T_H
#define AQHOME_REACT_PRGRULE_T_H
#include <gwenhywfar/gwenhywfarapi.h>
#include <gwenhywfar/testframework.h>
/**
* Internal tests for AQHREACT_PrgRule.
*/
int AQHREACT_PrgRule_AddTests(GWEN_TEST_MODULE *mod);
#endif

View File

@@ -0,0 +1,425 @@
/****************************************************************************
* 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 "./prgrule_p.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <ctype.h>
GWEN_LIST_FUNCTIONS(AQHREACT_PRGRULE, AQHREACT_PrgRule)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
const char *_readRule(AQHREACT_PRGRULE *prgRule, const char *s);
const char *_readField(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
const char *_readRangeOpenBegin(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
const char *_readInterval(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
const char *_readValueAndProbablyRange(const char *s, uint64_t *ptrBitField, int minValue, int maxValue);
const char *_readDouble(const char *s, double *ptrDouble);
int _valueOkay(int v, int minValue, int maxValue);
int _rangeOkay(int startValue, int endValue, int minValue, int maxValue);
void _setBitField(uint64_t *ptrBitField, int startValue, int endValue);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
AQHREACT_PRGRULE *AQHREACT_PrgRule_new(void)
{
AQHREACT_PRGRULE *prgRule;
GWEN_NEW_OBJECT(AQHREACT_PRGRULE, prgRule);
GWEN_LIST_INIT(AQHREACT_PRGRULE, prgRule);
return prgRule;
}
void AQHREACT_PrgRule_free(AQHREACT_PRGRULE *prgRule)
{
if (prgRule) {
GWEN_LIST_FINI(AQHREACT_PRGRULE, prgRule);
GWEN_FREE_OBJECT(prgRule);
}
}
double AQHREACT_PrgRule_GetValue(const AQHREACT_PRGRULE *prgRule)
{
return prgRule?prgRule->value:0.0;
}
int AQHREACT_PrgRule_Matches(const AQHREACT_PRGRULE *prgRule, int min, int hour, int dayOfMonth, int month, int dayOfWeek)
{
if ((prgRule->bitfieldMonth & (1L<<month)) &&
(prgRule->bitfieldDayOfMonth & (1L<<dayOfMonth)) &&
(prgRule->bitfieldDayOfWeek & (1L<<dayOfWeek)) &&
(prgRule->bitfieldHour & (1L<<hour)) &&
(prgRule->bitfieldMinute & (1L<<min)))
return 1;
return 0;
}
AQHREACT_PRGRULE *AQHREACT_PrgRule_List_FindMatchingRule(AQHREACT_PRGRULE_LIST *prgRuleList,
int min, int hour, int dayOfMonth, int month, int dayOfWeek)
{
AQHREACT_PRGRULE *prgRule;
prgRule=AQHREACT_PrgRule_List_First(prgRuleList);
while(prgRule) {
if (AQHREACT_PrgRule_Matches(prgRule, min, hour, dayOfMonth, month, dayOfWeek))
return prgRule;
prgRule=AQHREACT_PrgRule_List_Next(prgRule);
}
return NULL;
}
AQHREACT_PRGRULE_LIST *AQHREACT_PrgRule_ReadRules(const char *s)
{
AQHREACT_PRGRULE_LIST *prgRuleList;
prgRuleList=AQHREACT_PrgRule_List_new();
while(*s) {
AQHREACT_PRGRULE *prgRule;
const char *r;
prgRule=AQHREACT_PrgRule_new();
r=_readRule(prgRule, s);
if (r==NULL){
DBG_INFO(NULL, "Error parsing \"%s\"", s);
AQHREACT_PrgRule_free(prgRule);
AQHREACT_PrgRule_List_free(prgRuleList);
return NULL;
}
AQHREACT_PrgRule_List_Add(prgRule, prgRuleList);
s=r;
while(*s && isspace(*s))
s++;
if (*s==';')
s++;
}
if (AQHREACT_PrgRule_List_GetCount(prgRuleList)<1) {
AQHREACT_PrgRule_List_free(prgRuleList);
return NULL;
}
return prgRuleList;
}
const char *_readRule(AQHREACT_PRGRULE *prgRule, const char *s)
{
uint64_t v;
const char *r;
v=0;
r=_readField(s, &v, 0, 59);
if (r==NULL) {
DBG_ERROR(NULL, "Error reading MIN field at \"%s\"", s);
return NULL;
}
prgRule->bitfieldMinute=v;
s=r;
v=0;
r=_readField(s, &v, 0, 23);
if (r==NULL) {
DBG_ERROR(NULL, "Error reading HOUR field at \"%s\"", s);
return NULL;
}
prgRule->bitfieldHour=(uint32_t)(v & 0xffffffffl);
s=r;
v=0;
r=_readField(s, &v, 1, 31);
if (r==NULL) {
DBG_ERROR(NULL, "Error reading DAYOFMONTH field at \"%s\"", s);
return NULL;
}
prgRule->bitfieldDayOfMonth=(uint32_t)(v & 0xffffffffl);
s=r;
v=0;
r=_readField(s, &v, 1, 12);
if (r==NULL) {
DBG_ERROR(NULL, "Error reading MONTH field at \"%s\"", s);
return NULL;
}
prgRule->bitfieldMonth=(uint16_t)(v & 0xffffl);
s=r;
v=0;
r=_readField(s, &v, 0, 6);
if (r==NULL) {
DBG_ERROR(NULL, "Error reading DAYOFWEEK field at \"%s\"", s);
return NULL;
}
prgRule->bitfieldDayOfWeek=(uint8_t)(v & 0xffl);
s=r;
r=_readDouble(s, &(prgRule->value));
if (r==NULL) {
DBG_ERROR(NULL, "Error reading DOUBLE field at \"%s\"", s);
return NULL;
}
return r;
}
const char *_readField(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
{
while(*s && isspace(*s))
s++;
if (*s==0) {
DBG_INFO(NULL, "Premature field end");
return NULL;
}
while(s && *s) {
if (*s=='*') {
_setBitField(ptrBitField, minValue, maxValue);
s++;
}
else if (*s=='-') {
s=_readRangeOpenBegin(++s, ptrBitField, minValue, maxValue);
if (s==NULL) {
DBG_INFO(NULL, "here");
return NULL;
}
}
else if (*s=='/') {
s=_readInterval(++s, ptrBitField, minValue, maxValue);
if (s==NULL) {
DBG_INFO(NULL, "here");
return NULL;
}
}
else if (isdigit(*s)) {
s=_readValueAndProbablyRange(s, ptrBitField, minValue, maxValue);
if (s==NULL) {
DBG_INFO(NULL, "here");
return NULL;
}
}
else {
DBG_ERROR(NULL, "Unexpected character at \"%s\"", s);
return NULL;
}
if (isspace(*s)){
/* field done */
return s;
}
else if (*s==',') {
s++;
}
else {
DBG_ERROR(NULL, "Unexpected character at \"%s\"", s);
return NULL;
}
} /* while */
return s;
}
const char *_readRangeOpenBegin(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
{
int v=0;
if (!isdigit(*s)) {
DBG_ERROR(NULL, "Need at least start or end value for range");
return NULL;
}
while(isdigit(*s)) {
v*=10;
v+=(*s)-'0';
s++;
}
if (!_rangeOkay(minValue, v, minValue, maxValue)) {
DBG_INFO(NULL, "here");
return NULL;
}
_setBitField(ptrBitField, minValue, v);
return s;
}
const char *_readInterval(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
{
int v=0;
int i;
while(isdigit(*s)) {
v*=10;
v+=(*s)-'0';
s++;
}
if (v==0) {
DBG_ERROR(NULL, "Bad value 0 for interval (/0)");
return NULL;
}
for (i=minValue; i<maxValue; i+=v) {
_setBitField(ptrBitField, i, i);
}
return s;
}
const char *_readValueAndProbablyRange(const char *s, uint64_t *ptrBitField, int minValue, int maxValue)
{
int v=0;
while(isdigit(*s)) {
v*=10;
v+=(*s)-'0';
s++;
}
if (!_valueOkay(v, minValue, maxValue)) {
DBG_INFO(NULL, "here");
return NULL;
}
if (*s=='-') {
s=_readRangeOpenBegin(++s, ptrBitField, v, maxValue);
if (s==NULL) {
DBG_INFO(NULL, "here");
return NULL;
}
}
else {
if (!_valueOkay(v, minValue, maxValue)) {
DBG_INFO(NULL, "here");
return NULL;
}
_setBitField(ptrBitField, v, v);
}
return s;
}
const char *_readDouble(const char *s, double *ptrDouble)
{
const char *sStart;
int rv;
while(*s && isspace(*s))
s++;
sStart=s;
while(*s && (isdigit(*s) || *s=='.'))
s++;
rv=GWEN_Text_StringToDouble(sStart, ptrDouble);
if (rv<0) {
DBG_ERROR(NULL, "Error reading double: %d", rv);
return NULL;
}
return s;
}
int _valueOkay(int v, int minValue, int maxValue)
{
if (v<minValue) {
DBG_ERROR(NULL, "Value below minimum value (%d < %d)", v, minValue);
return 0;
}
if (v>maxValue) {
DBG_ERROR(NULL, "Value above maxmum value (%d > %d)", v, maxValue);
return 0;
}
return 1;
}
int _rangeOkay(int startValue, int endValue, int minValue, int maxValue)
{
if (startValue>endValue) {
DBG_ERROR(NULL, "Startvalue (%d) > endvalue (%d)", startValue, endValue);
return 0;
}
if (startValue<minValue || startValue>maxValue) {
DBG_ERROR(NULL, "Startvalue (%d) out of range (%d-%d)", startValue, minValue, maxValue);
return 0;
}
if (endValue>maxValue) {
DBG_ERROR(NULL, "Endvalue (%d) out of range (%d-%d)", endValue, minValue, maxValue);
return 0;
}
return 1;
}
void _setBitField(uint64_t *ptrBitField, int startValue, int endValue)
{
int i;
uint64_t m;
uint64_t v=0;
m=1l<<startValue;
for (i=startValue; i<=endValue; i++) {
v|=m;
m<<=1;
}
*ptrBitField|=v;
}
//#include "prgrule-t.c"

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