1161 Commits

Author SHA1 Message Date
Martin Preuss
636fc026aa more work on pages and ressources. 2025-12-15 21:12:51 +01:00
Martin Preuss
06f006ee42 n28: added lid to enclosure. 2025-12-15 16:21:44 +01:00
Martin Preuss
f9a6e4b1d7 n28: cut out upper bar from network connector holes 2025-12-12 00:04:38 +01:00
Martin Preuss
9e8a7c338d n28: more ventilation slits, decrease height, remove posts. 2025-12-11 23:54:14 +01:00
Martin Preuss
202439b8b6 n28: make ventilation slits bigger. 2025-12-11 23:01:02 +01:00
Martin Preuss
077e5cbedb added first enclosure for n28. 2025-12-11 22:48:56 +01:00
Martin Preuss
3083b433a3 aqhome-tool: started working on code to convert BMP file to assembler data. 2025-11-22 14:10:42 +01:00
Martin Preuss
dbba5617c7 avr: add touch handler routine to button module. 2025-11-22 14:10:06 +01:00
Martin Preuss
d8058dc711 avr: added 12x16 font, remove unneeded chars from fonts. 2025-11-18 23:03:00 +01:00
Martin Preuss
db6d28b869 control layout of windows via style.asm 2025-11-18 23:02:07 +01:00
Martin Preuss
b520ccb165 avr: fixed button code. 2025-11-18 19:14:40 +01:00
Martin Preuss
059654b16f smaller button routines. 2025-11-17 23:45:10 +01:00
Martin Preuss
5dcb48d671 incremented version. 2025-11-17 23:04:08 +01:00
Martin Preuss
f6e852be74 started working on static gui 2025-11-17 23:03:56 +01:00
Martin Preuss
defd869c26 removed some newlines. 2025-11-17 23:03:15 +01:00
Martin Preuss
ceaf0e0ee5 started working on buttons. 2025-11-17 23:02:55 +01:00
Martin Preuss
35557d7b22 renamed some vars. 2025-11-17 23:02:39 +01:00
Martin Preuss
102f4e65e3 added c03. 2025-11-17 23:02:19 +01:00
Martin Preuss
fbcfd65e7f more work on dialogs. 2025-11-17 23:01:26 +01:00
Martin Preuss
520f371560 com2w: added support for pin change interrupts on atmega644P 2025-11-17 23:00:40 +01:00
Martin Preuss
f8c914538a fixed urls. 2025-11-17 22:59:35 +01:00
Martin Preuss
049d651fec avr: improved modules ILI9341 and XPT2046. 2025-11-10 22:29:37 +01:00
Martin Preuss
ece4fe824a xpt2046 now basically works. 2025-11-10 19:33:40 +01:00
Martin Preuss
03f794b253 xpt2046: reading values works. 2025-11-10 15:55:31 +01:00
Martin Preuss
29f74c0eae avr: started working on xpt2046 module. 2025-11-10 14:49:23 +01:00
Martin Preuss
e82c1cbe5c avr: report motion and door values every 2m. 2025-11-10 14:48:36 +01:00
Martin Preuss
9a37bae20d aqhome-data: really fixed bug with creating bad device objects. 2025-11-07 17:26:03 +01:00
Martin Preuss
783815427a Prepared release 0.0.21 2025-11-07 17:04:18 +01:00
Martin Preuss
9300e515e7 more work on c02 and lcd drivers. 2025-11-07 17:04:08 +01:00
Martin Preuss
660f2502c1 aqhome-data: fixed a bug (was creating device instead of value object). 2025-11-07 17:03:32 +01:00
Martin Preuss
5b72686904 avr: added titled windows 2025-11-03 21:02:25 +01:00
Martin Preuss
e58e9b846c avr: work on simple GUI module to be used by node c02. 2025-11-03 17:23:40 +01:00
Martin Preuss
b4fee78ad8 aqhome-cgi: fixed a bug with RGBW values on pages. 2025-10-29 23:47:15 +01:00
Martin Preuss
06d287b53d prepared release 0.0.20. 2025-10-27 23:15:42 +01:00
Martin Preuss
7c5abc0f3b aqhome-cgi: more code sharing, adding page handling
allows to define your own pages with graphs, sensors and actors.
2025-10-27 23:15:28 +01:00
Martin Preuss
1b0145c97d Merge branch 'mp-2025_10-setdata_double' 2025-10-26 17:01:07 +01:00
Martin Preuss
31e677a4ae prepared release 0.0.19 2025-10-26 17:00:12 +01:00
Martin Preuss
4fb9e2ecdf fixed example file for apache2. 2025-10-26 16:59:58 +01:00
Martin Preuss
bb9f86211d decreased verbosity. 2025-10-26 16:59:01 +01:00
Martin Preuss
2f694f983a simplify graph code. 2025-10-26 16:58:29 +01:00
Martin Preuss
f858b5b3de added icons. 2025-10-26 00:17:25 +02:00
Martin Preuss
32bf7c0da4 set LimitNOFILE for unit aqhome-data
currently we use one file per value, so we need a larger max number of
open files.
2025-10-26 00:16:36 +02:00
Martin Preuss
aaf460ec6f add apache2 example conf for aqhome-cgi 2025-10-26 00:15:43 +02:00
Martin Preuss
ffc86b3e61 aqhome-cgi: changed graph modifier. 2025-10-26 00:14:54 +02:00
Martin Preuss
89228ed03b prepared release 0.0.18 2025-10-25 16:38:59 +02:00
Martin Preuss
ced89d9845 moved device files back into separate folder
otherwise installed module files are not update when compiling without AVR.
2025-10-25 16:38:44 +02:00
Martin Preuss
617c8551dc fixed a bug (was not closing connection on error). 2025-10-25 16:37:11 +02:00
Martin Preuss
c32be5cd38 increased verbosity. 2025-10-25 16:36:52 +02:00
Martin Preuss
7c320b210f more work on mdevices module. 2025-10-25 16:36:24 +02:00
Martin Preuss
0fa4c6c8b5 sk6812: set state to on when setting mode to auto.
need to have a better solution for this...
2025-10-24 16:39:53 +02:00
Martin Preuss
9caf468b86 avr: daily clear more stats. 2025-10-23 20:59:42 +02:00
Martin Preuss
55ac83820e fixed a bug.
was not accepting "0.0" as value.
2025-10-23 20:59:12 +02:00
Martin Preuss
cfa071c6de increased verbosity. 2025-10-23 20:58:54 +02:00
Martin Preuss
39dba4ccb8 more work on aqhome-cgi. 2025-10-23 20:58:40 +02:00
Martin Preuss
9fec57511a moved code for device module into its own folder. Implement graphs. 2025-10-21 23:42:11 +02:00
Martin Preuss
01aca2d3b7 aqcgi: use single value pages. 2025-10-10 00:29:52 +02:00
Martin Preuss
5a16117240 More work on aqhome-cgi. 2025-10-09 20:19:04 +02:00
Martin Preuss
d0c8b3b284 let setData use double values instead of strings.
this allows for storing value set with setData which can then be used in
the cgi module to retrieve the last value set.
2025-10-07 23:50:50 +02:00
Martin Preuss
1aeeed9845 Revert "remove string data."
This reverts commit 624b1648b5.
2025-10-07 18:09:53 +02:00
Martin Preuss
624b1648b5 remove string data. 2025-10-07 18:09:45 +02:00
Martin Preuss
1d6bb38d89 indent. 2025-10-07 00:28:23 +02:00
Martin Preuss
28cf031b46 n14, n25: add info to description files. 2025-10-07 00:28:06 +02:00
Martin Preuss
c9cfcc5dc4 added AQHOME_VALUETYPE_ONOFFAUTO 2025-10-07 00:27:41 +02:00
Martin Preuss
dd577be872 added stylesheet. 2025-10-07 00:27:15 +02:00
Martin Preuss
2a1af6f947 aqhome-cgi: added setdata.html 2025-10-07 00:26:57 +02:00
Martin Preuss
15728e27ca aqhome-cgi: increased session cookie duration. 2025-10-07 00:26:35 +02:00
Martin Preuss
0095b38c9f added OnOffAuto. 2025-10-07 00:26:07 +02:00
Martin Preuss
9a379f12b2 aqhome-tool: improved diagram. 2025-10-07 00:25:46 +02:00
Martin Preuss
023cabbc1b aqhome-cgi: add main menu. 2025-10-07 00:25:18 +02:00
Martin Preuss
a35211e427 Increase session cookie duration. 2025-10-07 00:24:39 +02:00
Martin Preuss
8629e52749 increase verbosity while debugging. 2025-10-01 23:25:14 +02:00
Martin Preuss
81e9078b80 avr: add MODE variable to sk6812 module.
this allows for better control over led strips.

Modes:
- AUTO: allow other modules to switch LED strip
- OFF:  force LED strip off
- ON:   force LED strip on
2025-10-01 23:24:50 +02:00
Martin Preuss
dafe32792a avr: fixed bug in ma_light 2025-10-01 23:22:41 +02:00
Martin Preuss
3f9e16f038 aqhome-tool: allow for data spec for timestamps (YYYY/MM/DD-HH:MM:SS) 2025-10-01 23:21:50 +02:00
Martin Preuss
5bd1e06850 aqhome-tool: visualize data points using aqdiagram. 2025-10-01 23:21:12 +02:00
Martin Preuss
5040ccc065 sharply decrease verbosity. 2025-10-01 23:20:04 +02:00
Martin Preuss
494e7b3fbc aqhome-data: send data in multiple messages. remove limits. 2025-10-01 23:19:48 +02:00
Martin Preuss
8d22d386b4 updated readme. 2025-09-22 22:48:49 +02:00
Martin Preuss
0f712717d8 sk6812: added readme. 2025-09-22 22:48:37 +02:00
Martin Preuss
f201ac4bd6 add to apidoc. 2025-09-22 22:47:20 +02:00
Martin Preuss
408b173ae1 ma_light: add "malstate".
report state change (i.e. when switching LED on/off).
2025-09-22 22:44:39 +02:00
Martin Preuss
12a64b8c91 log raw type 2025-09-22 22:41:57 +02:00
Martin Preuss
6920117ba1 immediately apply ontime setting. 2025-09-22 20:27:44 +02:00
Martin Preuss
8d188976eb fixed data types 2025-09-22 20:27:09 +02:00
Martin Preuss
9770cd8d6d indent. 2025-09-22 20:26:36 +02:00
Martin Preuss
68829f6428 add comments. 2025-09-22 20:26:30 +02:00
Martin Preuss
cc00f8e64a avr: remove unneeded code. 2025-09-22 20:26:16 +02:00
Martin Preuss
d08d30d297 shorten waiting time for retry 2025-09-22 20:25:31 +02:00
Martin Preuss
90f0590d18 aqhome-nodes: log outbound messages. too. 2025-09-22 20:25:08 +02:00
Martin Preuss
732eb4f53d avr: increased version number. 2025-09-22 15:09:59 +02:00
Martin Preuss
666e7690a6 avr: improved module SK6812 and app MA_LIGHT.
- RGBW values are now only set by SK6812
- MA_LIGHT can now also take into account a brightness value reported
  by any node to switch on light only after dawn
2025-09-22 15:09:45 +02:00
Martin Preuss
42e477098c aqhome-cgi: added code to init aqhome-cgi environment. 2025-09-22 11:34:43 +02:00
Martin Preuss
162d6d9f4e aqhome-cgi: decreased verbosity. 2025-09-20 01:57:05 +02:00
Martin Preuss
13ee628b8d aqhome-cgi: removed writing of debug log. 2025-09-20 01:56:25 +02:00
Martin Preuss
124815079a aqhome-cgi: implemented adding roles for users. 2025-09-20 01:52:50 +02:00
Martin Preuss
c1afb05250 aqhome-cgi: put role management for user into single dialog. 2025-09-20 00:54:32 +02:00
Martin Preuss
5c24750acc aqhome-cgi: add user editing and adding code. 2025-09-19 23:45:42 +02:00
Martin Preuss
4d6edf3fb6 aqhome-cgi: add error message. 2025-09-19 23:45:12 +02:00
Martin Preuss
b13bace79f aqhome-cgi: add sort functions. 2025-09-19 23:44:47 +02:00
Martin Preuss
60a3f1f324 aqhome-cgi: add functions for loading users and modules. 2025-09-19 23:44:14 +02:00
Martin Preuss
860310de7f removed comment. 2025-09-19 23:43:37 +02:00
Martin Preuss
65c89fab07 aqhome-cgi: more work on userEdit dialog 2025-09-19 16:34:50 +02:00
Martin Preuss
8da865928a more code sharing. 2025-09-18 23:49:04 +02:00
Martin Preuss
6b39e0bfb1 aqhome-cgi: started working on users module. 2025-09-18 23:48:47 +02:00
Martin Preuss
124e471364 add some commonnly used functions. 2025-09-18 00:16:14 +02:00
Martin Preuss
8178901ffb minor change in message. 2025-09-16 23:23:15 +02:00
Martin Preuss
3fd460637a remove unneeded code. 2025-09-16 23:21:01 +02:00
Martin Preuss
5cd308d10c only create links for values if perms match. 2025-09-16 23:19:45 +02:00
Martin Preuss
a0e18c80dc shorten code. 2025-09-16 23:13:09 +02:00
Martin Preuss
b0ce010dc8 adapt to latest changes, use generic request handlers. 2025-09-16 23:09:18 +02:00
Martin Preuss
8805712aea removed unneeded code, make _requestTable private to source file. 2025-09-16 23:08:51 +02:00
Martin Preuss
a1d6f80c36 add AQH_ModService_HandleRequestWithTable(). Shorten code. 2025-09-16 22:44:27 +02:00
Martin Preuss
3e00ac4dfb aqhome-cgi: fixed returning a static file. 2025-09-15 23:51:10 +02:00
Martin Preuss
fb365c7e1d removed unneeded code. 2025-09-15 23:50:39 +02:00
Martin Preuss
d7145198a1 fixed login handling. 2025-09-15 19:35:09 +02:00
Martin Preuss
9f6adf7a98 renew session cookie. 2025-09-15 19:34:56 +02:00
Martin Preuss
348c3a8853 aqhome-cgi: moved files. 2025-09-15 19:15:51 +02:00
Martin Preuss
7f301d271b aqhome-cgi: more work 2025-09-15 18:47:16 +02:00
Martin Preuss
8f2bd23dc5 added modality "onOff". 2025-09-15 18:06:33 +02:00
Martin Preuss
8db57a7875 aqhome-nodes: set correct modality for stats. 2025-09-15 18:06:16 +02:00
Martin Preuss
785f479921 aqhome-data: update value info when new data arrives. 2025-09-15 18:05:39 +02:00
Martin Preuss
61f69f45b0 More work on aqhome-cgi. 2025-09-15 18:04:44 +02:00
Martin Preuss
90f6ce73e4 more work on aqhome-cgi 2025-09-13 02:17:45 +02:00
Martin Preuss
3bda196b79 More work on aqhome-cgi.
listing devices and values works.
2025-09-12 15:09:00 +02:00
Martin Preuss
757f4fca4f changed link order (otherwise wouldn't work). 2025-09-12 15:08:14 +02:00
Martin Preuss
54b4693b2e fix for build system. 2025-09-12 15:07:49 +02:00
Martin Preuss
1c8e9134bf com2wi2: added some comments 2025-09-12 15:07:14 +02:00
Martin Preuss
1915133093 temporarily disabled r05 device. 2025-09-12 15:06:56 +02:00
Martin Preuss
53e076c2ad added argument "DEVICENAME" to GETDEVICES request.
faster if only a specific device is requested.
2025-09-12 15:06:26 +02:00
Martin Preuss
3ea7170473 tried early reading ports (still doesn't work for 2nd port). 2025-09-09 01:01:43 +02:00
Martin Preuss
0dc9f0330c increase irq frequency (now every 10us). 2025-09-09 00:47:24 +02:00
Martin Preuss
17a7dddd02 removed unneeded defs. 2025-09-09 00:47:06 +02:00
Martin Preuss
a81acf7b19 com2wi2: test with two interfaces. 2025-09-08 23:34:38 +02:00
Martin Preuss
d7e4ee4cca fixed bugs on big systems. 2025-09-08 22:06:09 +02:00
Martin Preuss
5866a55067 use com2wi in S03 2025-09-08 22:05:52 +02:00
Martin Preuss
40282486f6 added com2wi2 (will become com2wi later). 2025-09-08 22:05:28 +02:00
Martin Preuss
2b2c41867f com2w: fixed a bug (on CRC error the buffer is not yet allocated). 2025-09-08 22:04:53 +02:00
Martin Preuss
679865f68b Fixed some bugs. 2025-09-08 22:04:15 +02:00
Martin Preuss
5b459ef5ac Merge branch 'mp-2025_07-improvr_uart_hw2' 2025-09-07 12:56:19 +02:00
Martin Preuss
9685e45861 incremented version number. 2025-09-07 11:50:33 +02:00
Martin Preuss
fbda9cf228 n28 has no motion sensor. 2025-09-07 11:50:21 +02:00
Martin Preuss
552cd5b2ae moved device info files to their respective device folders. 2025-09-07 11:50:11 +02:00
Martin Preuss
3926b01b13 remove old stats from device info files. 2025-09-07 11:49:39 +02:00
Martin Preuss
ffaa27ca24 aqhome-data: limit number of open files. 2025-09-07 11:48:55 +02:00
Martin Preuss
9a1c5799aa decreased verbosity. 2025-09-07 11:48:35 +02:00
Martin Preuss
85e66c352d n26, n27: use slow blinking LED_SIMPLE module instead of LED_ACTIVITY. 2025-09-07 01:44:05 +02:00
Martin Preuss
d1b67a4819 devicestate: add valuename for improved filtering. 2025-09-07 01:43:31 +02:00
Martin Preuss
51d55128a9 Decreased verbosity. 2025-09-07 01:43:13 +02:00
Martin Preuss
52a5078706 added flash instructions for n28 and t04. 2025-09-06 00:43:05 +02:00
Martin Preuss
2d15091b0c avr: updated uartfd0 (copied from uartfd1) 2025-09-06 00:42:49 +02:00
Martin Preuss
81598881d1 uartfd1: fixed typos. 2025-09-06 00:42:20 +02:00
Martin Preuss
4a74a9990f minor fixes. 2025-09-06 00:41:58 +02:00
Martin Preuss
6c1e8a45ff com2wn: only trigger led if LED_ACTIVITY is defined. 2025-09-06 00:41:17 +02:00
Martin Preuss
808a7c4258 com2w: check COM_ACCEPT_ALL_DEST 2025-09-06 00:40:42 +02:00
Martin Preuss
80ca6e0ac5 avr: added t04. 2025-09-06 00:40:09 +02:00
Martin Preuss
0989543b1b avr: accept all dest addresses on routers/forwarders. 2025-09-06 00:39:50 +02:00
Martin Preuss
36195f88e0 r05: added missing defs. 2025-09-06 00:39:08 +02:00
Martin Preuss
3b916facf4 avr: added device n28. 2025-09-06 00:38:43 +02:00
Martin Preuss
9d59bf31e6 avr: reduce number of stats messages. 2025-09-06 00:38:26 +02:00
Martin Preuss
a256c6261c decreased verbosity. 2025-09-06 00:38:08 +02:00
Martin Preuss
21830321f7 dataclient: fixed a typo. 2025-09-06 00:37:57 +02:00
Martin Preuss
eb27d223fb aqhome-tool: added command "devicestate". 2025-09-06 00:37:39 +02:00
Martin Preuss
33cae3ab26 incremented version. 2025-09-06 00:36:54 +02:00
Martin Preuss
1612065d8a uartfd1: add comment. 2025-09-01 23:25:39 +02:00
Martin Preuss
eac37b9927 com2w: increase buffer size. 2025-09-01 23:25:20 +02:00
Martin Preuss
b95a4fa872 avr: more work on com2wn module. 2025-09-01 23:25:02 +02:00
Martin Preuss
81669a5442 avr: first try irq driven com2w interface.
probably doesn't work like that but that's for later.
2025-09-01 23:24:15 +02:00
Martin Preuss
3270a71cb0 com2w: simplified module. 2025-09-01 23:23:30 +02:00
Martin Preuss
5ae7f821e4 s03: more work. 2025-09-01 23:22:57 +02:00
Martin Preuss
224aca5c41 n21: don't send ADC values. 2025-09-01 23:22:21 +02:00
Martin Preuss
9600be78a5 more work on m644p. 2025-09-01 23:21:52 +02:00
Martin Preuss
246d00a220 avr: allow for big flash roms. 2025-09-01 23:21:25 +02:00
Martin Preuss
809439fd26 aqhome-nodes: handle new statistics messages. 2025-09-01 23:20:38 +02:00
Martin Preuss
86741a0e50 t03: use forwarder app. 2025-08-25 14:13:08 +02:00
Martin Preuss
41843cbab9 avr: added forwarder app.
simpler than router app, just for forwarding messages between interfaces.
mainly used by t03.
2025-08-25 14:12:58 +02:00
Martin Preuss
64854506e5 fixed recvstats message handling. 2025-08-25 14:11:57 +02:00
Martin Preuss
463385a296 increment version. 2025-08-25 11:26:19 +02:00
Martin Preuss
b97fc98bc1 t03: use led_activity 2025-08-25 11:26:08 +02:00
Martin Preuss
9b86aea7ed use led_activity. 2025-08-25 11:25:53 +02:00
Martin Preuss
5bfb49d9e1 simplified code, remove unused code. 2025-08-25 11:25:30 +02:00
Martin Preuss
32a0ad5eae added comments, removed unused code. 2025-08-25 11:24:53 +02:00
Martin Preuss
65593f95ad use led_activity module. 2025-08-25 11:23:35 +02:00
Martin Preuss
ec3774c7a5 fixed a bug in field order 2025-08-25 11:22:54 +02:00
Martin Preuss
4bd462ea59 use new led modules. 2025-08-25 10:25:53 +02:00
Martin Preuss
77d3a201f5 improved uartfd1. 2025-08-25 10:24:48 +02:00
Martin Preuss
127525e3ec buffer: gather statistics 2025-08-25 10:23:47 +02:00
Martin Preuss
465e750e3c avr: activate new led modules. 2025-08-25 10:22:50 +02:00
Martin Preuss
6e062d3f60 improve stats reporting
now again use three messages to transmit stats (much more efficient than
sending single values, also more acurate).
2025-08-25 10:22:05 +02:00
Martin Preuss
7efaf720cc com2w: work in COM2W_Run, not in COM2W_Every100ms 2025-08-25 10:20:13 +02:00
Martin Preuss
66ac029a69 avr: introduce two more led modules.
- led_signal: blink 8 status bits (short=0, long=1)
- led_activity: blink for a short period after a trigger, e.g. for network
  activity
2025-08-25 10:19:23 +02:00
Martin Preuss
bae188ddee t03: added firmware using new uart_fd module. 2025-08-23 00:10:01 +02:00
Martin Preuss
87788fa93c avr: wait for free CLK line on error when in flash mode 2025-08-23 00:09:39 +02:00
Martin Preuss
5013bb8e29 avr network app: only respond to ping request if dest address matches. 2025-08-23 00:09:01 +02:00
Martin Preuss
08d420a871 avr: added module uart_fd
This module uses hardware UART of MCUs in full duplex mode.
2025-08-23 00:08:25 +02:00
Martin Preuss
3a7951be16 com2w: added some recovery code, improved protocol.
after sending a byte wait for the same time when sending the last bit as
with the other bits (otherwise the last bit might get lost by slower
devices).
2025-08-20 23:27:17 +02:00
Martin Preuss
6a3f6be8a9 avr: improved timing for sending bytes via com2w interface.
Still quite different among devices but that's just prove that the protocol
works without prior synchronisation.
2025-08-20 20:48:55 +02:00
Martin Preuss
6c3926c1e4 avr: added a note regarding timing. 2025-08-19 23:36:58 +02:00
Martin Preuss
8467408fd8 avr: increased waittimes for com2w (too fast for 1MHz nodes). 2025-08-19 23:36:44 +02:00
Martin Preuss
6bb499740d avr: use 16bit for brightness (actually, ADC only presents 10 bit). 2025-08-19 23:03:43 +02:00
Martin Preuss
fc073696f8 avr: tweaked timing for com2w transport layer. 2025-08-19 23:03:15 +02:00
Martin Preuss
102996f69f avr: switched most devices to com2w interface now. 2025-08-18 18:47:13 +02:00
Martin Preuss
f9d721ac02 removed unused device nodes.
- n00
- n06
- n11
- n12
- n15
- n17
- n18
- n19
- n20
- n22
- n23
- r02
- r04
- x03
2025-08-18 18:46:46 +02:00
Martin Preuss
6bf8f03fbb added node S03. 2025-08-18 18:07:34 +02:00
Martin Preuss
fa404a5fcf increase number of runs to 10 (was: 2) 2025-08-18 18:06:56 +02:00
Martin Preuss
3592a745d3 introduce app "hub" 2025-08-18 18:06:35 +02:00
Martin Preuss
7c01aa255f split wait for 50us (value 50000 doesn't work for 20MHz nodes). 2025-08-18 18:05:12 +02:00
Martin Preuss
d9e7d4df81 com2w*: allow for higher frequencies than 8MHz
to be used with node S03, which is run at 20MHz.
2025-08-18 18:04:30 +02:00
Martin Preuss
9b1badb310 avr: introduce reference counter for network buffers. 2025-08-18 18:03:24 +02:00
Martin Preuss
0dc4a3a952 avr: started adding COM2WN module. 2025-08-04 22:28:49 +02:00
Martin Preuss
32bd3bc2f9 added r06 again. 2025-08-04 22:26:26 +02:00
Martin Preuss
3f2ae9dd03 Revert "started working on mqtt support in aqhome-nodes"
This reverts commit 7fbc616ce4.
2025-08-03 00:56:46 +02:00
Martin Preuss
7fbc616ce4 started working on mqtt support in aqhome-nodes 2025-08-03 00:56:22 +02:00
Martin Preuss
8c13f9fdf7 avr: started adding com2wn module (multiple com2w devices in one) 2025-08-03 00:55:52 +02:00
Martin Preuss
6c5dc21f6a avr: started adding r06 device. 2025-08-03 00:55:21 +02:00
Martin Preuss
eb1392f3fd avr: fixed apidoc. 2025-08-03 00:54:45 +02:00
Martin Preuss
2b25fed2cd com2w1: follow changes in com2w0. 2025-07-21 18:25:17 +02:00
Martin Preuss
66b298d977 guard some changes on network data with CLI. 2025-07-20 23:15:46 +02:00
Martin Preuss
1091ec1dee read CLK and DATA early. 2025-07-20 23:15:22 +02:00
Martin Preuss
d0b5f84316 adapt com2w1 from com2w0 (the latter is proven to work now). 2025-07-20 00:15:13 +02:00
Martin Preuss
ae1e4c3e37 avr: bootloader works with new com2w code. 2025-07-20 00:05:53 +02:00
Martin Preuss
78cbbf334e com2w: alloc buffer after receiving message. receiving works now. 2025-07-19 17:47:52 +02:00
Martin Preuss
c8c12bb892 t03: use router app. 2025-07-19 17:08:39 +02:00
Martin Preuss
ae1853ba62 enable sending messages. 2025-07-19 17:08:19 +02:00
Martin Preuss
e8423ae97f check dest address for ping requests. 2025-07-19 17:07:52 +02:00
Martin Preuss
597504fb08 t03: added test firmware. 2025-07-19 15:02:46 +02:00
Martin Preuss
59a0962420 added com2w0 2025-07-19 15:02:31 +02:00
Martin Preuss
323a5b76be sync test. 2025-07-19 09:48:56 +02:00
Martin Preuss
b7234a6da2 start using new COM module in n27, r05 and t03. 2025-07-19 09:47:23 +02:00
Martin Preuss
4a5ba97b85 increase buffer size. 2025-07-19 09:46:06 +02:00
Martin Preuss
bdd710fc5c avr: started working on new SPI-like COM protocol.
use a clock and a data line to introduce synchronisation into the
protocol to be able to work with the wide range of mcu speeds (no need for
exact timing, no need for exact calibration).
2025-07-19 09:42:02 +02:00
Martin Preuss
535a695c50 limit number of loops inside main_runLoop. 2025-07-12 20:16:34 +02:00
Martin Preuss
357ffe4e17 comonuartX: handle multiple messages in one run.
still there are NOBUF errors...
2025-07-12 20:09:40 +02:00
Martin Preuss
349b4a929a use in instead of inr (SREG is always inside normal io space). 2025-07-12 20:08:54 +02:00
Martin Preuss
a06d245345 r05: disable OWI and ds18b20 modules (no time to handle those), increase buffers 2025-07-12 20:08:30 +02:00
Martin Preuss
83225c453d Use uid to determine initial wait time after re-enum request. 2025-07-12 20:07:33 +02:00
Martin Preuss
fa94190345 fixed message description for FLASH_DATA. 2025-07-12 00:22:18 +02:00
Martin Preuss
68aa3beab8 incremented firmware version. 2025-07-12 00:21:59 +02:00
Martin Preuss
3cd23d5f60 reduce size of net buffers from 32 to 28.
allows us to use 10 buffers in R05.
2025-07-12 00:21:47 +02:00
Martin Preuss
003f53b0b7 added READMEs 2025-07-11 23:25:19 +02:00
Martin Preuss
c1ea4212f2 Moved NET_BUFFERS_SIZE to network/defs.asm
- Changing this value requires changing the routine NET_Buffer_Locate
  so its best to keep both in one module.
- Redefining it in the individual devices doesn't change the code in
  NET_Buffer_Locate
2025-07-11 23:25:10 +02:00
Martin Preuss
3283a38981 Release 0.9.12. 2025-07-11 23:22:09 +02:00
Martin Preuss
3054274da5 Fixed stats value list. 2025-07-11 23:21:50 +02:00
Martin Preuss
2d84198e54 incremented version. 2025-07-07 21:53:59 +02:00
Martin Preuss
f3544f5e93 fixed apidoc. 2025-07-07 21:53:48 +02:00
Martin Preuss
04c02b5e33 fixed a bug. 2025-07-07 21:53:35 +02:00
Martin Preuss
b709b7e624 updated node doc. 2025-07-07 21:45:36 +02:00
Martin Preuss
8c397dd6b2 added missing includes. 2025-07-07 21:45:19 +02:00
Martin Preuss
6e923ce075 improved apidoc. 2025-07-07 21:44:54 +02:00
Martin Preuss
245d44c05d improved "run" code. 2025-07-07 21:44:40 +02:00
Martin Preuss
4f497fc41a removed unneeded code. 2025-07-07 21:43:41 +02:00
Martin Preuss
e32e8e7c13 use Eeprom_WriteByteIfChanged 2025-07-07 21:43:28 +02:00
Martin Preuss
f3020562bf incremented firmware version. 2025-07-07 16:06:31 +02:00
Martin Preuss
fbb710a4e3 added TLV code for EEPROM (not used for now). 2025-07-07 16:06:18 +02:00
Martin Preuss
cbd498150f avr: fully implemented router functionality in network and router app. 2025-07-07 16:05:53 +02:00
Martin Preuss
691ee3c71b incremented firmware version. 2025-07-06 20:22:38 +02:00
Martin Preuss
280d5828bf fixed a possible problem.
don't reboot on broadcast request.
2025-07-06 20:22:19 +02:00
Martin Preuss
6ecc1721b0 r05: no longer use APPS_NETWORK.
Implemented some of those features in APP_ROUTER like PING, REBOOT.
2025-07-06 20:21:51 +02:00
Martin Preuss
1824e8ccdf Increment packets-out counter. 2025-07-06 20:01:35 +02:00
Martin Preuss
0a45e38939 make router functionality of r05 an app. 2025-07-06 18:21:48 +02:00
Martin Preuss
81b008af0c r05, comOnUart0 and comOnUart1 work! 2025-07-06 17:19:59 +02:00
Martin Preuss
439e787d37 added values to device description file for t03. 2025-07-06 14:40:14 +02:00
Martin Preuss
d242d63c2e disable irqs when releasing buffer. 2025-07-06 14:39:48 +02:00
Martin Preuss
86982d0000 reuse code. 2025-07-06 14:39:16 +02:00
Martin Preuss
f549ef5d27 added debug output. 2025-07-06 14:39:02 +02:00
Martin Preuss
fe2eaafb7b add devices r04 and r05 to flash script. 2025-07-06 14:38:43 +02:00
Martin Preuss
caf149fe8b minor changes to unify modules. 2025-07-06 14:38:21 +02:00
Martin Preuss
c540135705 fixed a bug. 2025-07-06 14:37:10 +02:00
Martin Preuss
734c237666 sort counters to simplify stats app. 2025-07-06 14:36:50 +02:00
Martin Preuss
85d445ec61 t03: use comonuart0 from uart_hw2 2025-07-06 14:35:52 +02:00
Martin Preuss
b059f4a56e added node r05. 2025-07-06 14:35:22 +02:00
Martin Preuss
b56ab22117 more work on test firmware for r04 2025-07-06 14:34:55 +02:00
Martin Preuss
5bda393b10 added valueids for stats from 2nd network device. 2025-07-06 12:24:46 +02:00
Martin Preuss
4ad508eca6 n27: removed driver for SGP30 (is now used externally in n26). 2025-07-06 12:23:49 +02:00
Martin Preuss
7f856dacf6 use uart_hw2 now. 2025-07-06 12:22:50 +02:00
Martin Preuss
b6ba56a564 added code to read calibration data. 2025-07-06 12:22:22 +02:00
Martin Preuss
f930b846c2 uart_hw2: basically works, but skips messages. 2025-07-06 12:21:41 +02:00
Martin Preuss
fc5394a5c9 avr: added comonuart0
works fine so far.
2025-07-03 22:12:40 +02:00
Martin Preuss
725ff96425 added test firmware for r04. 2025-07-03 22:11:27 +02:00
Martin Preuss
bfd0cd77a9 comonuart1: fixed bit names, disable IRQ when starting to write. 2025-07-03 00:16:29 +02:00
Martin Preuss
548a7634b7 comonuart1: undid some of the latest changes. 2025-07-02 00:10:46 +02:00
Martin Preuss
3e4637e174 comonuart1: try to solve timing/irq problem. 2025-07-02 00:08:01 +02:00
Martin Preuss
20b7c3f50d avr: new uart_hw2 module comonuart1 works now. 2025-07-01 00:52:44 +02:00
Martin Preuss
9206341032 started working on improved UART_HW module. 2025-06-30 21:29:05 +02:00
Martin Preuss
6383d18e0e avr: added comonuart1. 2025-06-29 22:32:43 +02:00
Martin Preuss
cfc7dc6320 avr: added r04. 2025-06-29 22:31:51 +02:00
Martin Preuss
72acef3aaf Increased max message size. 2025-06-26 00:31:11 +02:00
Martin Preuss
850975a85b avr: fixed a bug (was not calling sysOnEveryDay) 2025-06-26 00:18:33 +02:00
Martin Preuss
68ee246216 aqhome-tool: added some commands
- getFirstData
- getLastData
- getPeriodData
2025-06-26 00:18:05 +02:00
Martin Preuss
2ba802bb06 More work on dataclient library. 2025-06-26 00:16:52 +02:00
Martin Preuss
688129cf08 added n27 to flash script. 2025-06-25 00:09:41 +02:00
Martin Preuss
81721ab7eb avr: added device n27. 2025-06-25 00:05:47 +02:00
Martin Preuss
bf50871208 n25: use pin PA5 to select led strip driver to use
does nothing for now but will later allow to select between SK6812 and SPI.
2025-06-25 00:05:19 +02:00
Martin Preuss
cbf88e05fe added "mode" argument to "getdata" command. 2025-06-25 00:03:58 +02:00
Martin Preuss
a808450fa2 add dataclient sublib to be used by multiple tools. 2025-06-25 00:03:09 +02:00
Martin Preuss
033b7cd4db getvalues: allow for filtering value list on server. 2025-06-25 00:02:32 +02:00
Martin Preuss
cd6a918533 s_getvalues: allow for filtering output list. 2025-06-25 00:01:52 +02:00
Martin Preuss
63ebcbadc9 added msg "getValues" 2025-06-25 00:01:12 +02:00
Martin Preuss
b2802a37aa added AQH_Storage_GetFirstNDataPoints() 2025-06-25 00:00:39 +02:00
Martin Preuss
1e95b317bf incremented firmware version. 2025-06-23 19:28:35 +02:00
Martin Preuss
78fec171f9 sgp30: measure every second, increases accuracy. 2025-06-23 19:28:20 +02:00
Martin Preuss
8bfaabcf27 read 10bit brightness (instead of 8 bit). 2025-06-23 19:27:53 +02:00
Martin Preuss
2b08847cf7 added n26. 2025-06-23 19:21:58 +02:00
Martin Preuss
9ea722607f avr: added brightness sensor 2025-06-23 19:21:49 +02:00
Martin Preuss
409155f0d0 increment version. 2025-06-17 00:14:14 +02:00
Martin Preuss
026a943648 avr: adde stats messages to device definitions. 2025-06-17 00:13:52 +02:00
Martin Preuss
aeb6df5685 avr: reduce number of messages send.
- sensor report interval 60->120s
- stats report interval 11-31 mins
2025-06-17 00:13:33 +02:00
Martin Preuss
06886e0094 aqhome-cgi: more work 2025-06-16 23:31:53 +02:00
Martin Preuss
b498a445b2 added missing files, n26, increased firmware version. 2025-06-16 23:31:38 +02:00
Martin Preuss
876e9ac7b9 avr: added n14 back in. 2025-06-16 23:29:56 +02:00
Martin Preuss
a89b875872 avr: added NET_Interface_ResetStats and call it daily. 2025-06-16 23:29:18 +02:00
Martin Preuss
0ce70e48b1 avr: added type "light" 2025-06-16 23:27:37 +02:00
Martin Preuss
bf583a1aa5 avr: incremented version. 2025-06-15 17:47:45 +02:00
Martin Preuss
1533f82ec4 n21: send value of door sensor every minute
this will allow us to determine the best value for a adc limit to detect
open/closed windows and doors.
2025-06-15 17:47:34 +02:00
Martin Preuss
8a43bc252f tcrt1000: decreased limit from 170 to 100 2025-06-15 17:46:06 +02:00
Martin Preuss
ffcc5c0d9f some minor work on gui/win modules. 2025-06-12 23:30:36 +02:00
Martin Preuss
c51043d72a more work on aqhome-cgi. 2025-06-12 23:30:08 +02:00
Martin Preuss
2cb534df85 more work on GUI module. 2025-06-05 22:48:22 +02:00
Martin Preuss
4f30623f2d increased stacksize. 2025-06-05 22:47:54 +02:00
Martin Preuss
b9ac7c65fa include CCS811 in reportsensors app. 2025-06-05 22:46:55 +02:00
Martin Preuss
cef487fb3a re-enabled node n19. adapted n20. 2025-06-05 22:45:52 +02:00
Martin Preuss
284539fd52 re-enabled CCS811 module, adapted to latest changes. 2025-06-05 22:44:27 +02:00
Martin Preuss
08a1313ba5 More work on layout code. 2025-06-04 23:56:42 +02:00
Martin Preuss
7349014bd6 Make coordinates 16 bit again. 2025-06-04 23:56:29 +02:00
Martin Preuss
12cfe2ff4b Fixed a bug.
Need to make room for stack.
2025-06-04 23:56:04 +02:00
Martin Preuss
ae1892f196 share code. 2025-06-03 00:00:14 +02:00
Martin Preuss
dbad237826 fixed a typo. 2025-06-02 23:41:38 +02:00
Martin Preuss
56e222a97e split WID_SIGNAL_GETMINSIZE into two signals. 2025-06-02 23:38:08 +02:00
Martin Preuss
888792a201 more work on gui code. 2025-06-02 23:29:50 +02:00
Martin Preuss
41867ed01a added missing files. 2025-06-02 23:29:06 +02:00
Martin Preuss
b82e0d02df avr: more general approach to fonts. 2025-06-02 21:13:44 +02:00
Martin Preuss
cd1fce313e avr: added some unit tests for LIST and TREE. 2025-06-02 21:13:01 +02:00
Martin Preuss
5153bd2f69 removed signal "CREATED".
unsure when this would be called:
- when the basic object is called?
- when the last derived object is called?
better remove it to avoid complications...
2025-06-02 01:04:49 +02:00
Martin Preuss
d4ad6844e3 ili9341: save X 2025-06-02 01:03:23 +02:00
Martin Preuss
4f610c68a2 added more fonts and corresponding code. disabled test code in ili9341 module. 2025-06-02 01:03:03 +02:00
Martin Preuss
3582659018 added more bigcall/bigjmp as we go along with larger code. 2025-06-02 01:02:06 +02:00
Martin Preuss
1f537849a9 incremented version. 2025-06-01 22:39:56 +02:00
Martin Preuss
e6d0118ff3 n25: fixed interrupt table (was still for t85!). 2025-06-01 22:38:48 +02:00
Martin Preuss
ff7d47e155 avr: adapted to latest changes. 2025-06-01 22:38:17 +02:00
Martin Preuss
8ae50a8d60 fixed flashnode.sh for n24, added n25 2025-06-01 22:37:46 +02:00
Martin Preuss
08411d9430 NET_IFACE_OFFS_HANDLED_LOW no longer exists. 2025-06-01 22:37:26 +02:00
Martin Preuss
9bd3182bd5 simplified timer and sleep setup code for AtTiny84. 2025-06-01 22:36:59 +02:00
Martin Preuss
c45eb6cca2 fixed blinkled fn: always switch LED port to output. 2025-06-01 22:36:26 +02:00
Martin Preuss
b229b39ab8 c02: started working on AtMEGA 644P based node. 2025-06-01 19:26:31 +02:00
Martin Preuss
87b5e01581 add missing include. 2025-06-01 19:25:57 +02:00
Martin Preuss
8188f33345 uart_bitbang2: introduced macros for ATTN irq setup. 2025-06-01 19:25:47 +02:00
Martin Preuss
7403b6650b n24: include common/calls.asm 2025-06-01 19:25:16 +02:00
Martin Preuss
6bbf2ba788 c01: use UART_BitBang_PcintIsr 2025-06-01 19:24:49 +02:00
Martin Preuss
06b0ed8551 c01: fixed include. 2025-06-01 19:24:13 +02:00
Martin Preuss
ada19028e0 dont use old constant. 2025-06-01 19:23:38 +02:00
Martin Preuss
1e5de0da23 flash: use 16-bit counters. 2025-06-01 19:22:04 +02:00
Martin Preuss
bb14dd4c22 introduce macros bigjmp and bigcall for intermodule calls/jmps
translates to rjmp/rcall on MCUs with up to 8K flash and to jmp/call
on others.
2025-06-01 19:18:25 +02:00
Martin Preuss
188e7da379 incremented firmware version. 2025-06-01 00:21:52 +02:00
Martin Preuss
982c9bd649 re-enable some modules and apps on n20. 2025-06-01 00:21:37 +02:00
Martin Preuss
fec37bd221 disable debug code. 2025-06-01 00:21:21 +02:00
Martin Preuss
120e3e1e6b uart bitbang2 now also works on c01! 2025-06-01 00:21:07 +02:00
Martin Preuss
8d1661d8e4 improved output from "getdevices" command. 2025-06-01 00:20:28 +02:00
Martin Preuss
18f61f4d63 added include for debug functions (commented-out). 2025-05-31 15:37:31 +02:00
Martin Preuss
be74442e7f receiving works again. 2025-05-31 15:36:52 +02:00
Martin Preuss
061119819f sending works again with n20. 2025-05-31 14:20:05 +02:00
Martin Preuss
0b8cb929b7 split uart_bitbang2 into multiple files. 2025-05-30 17:03:35 +02:00
Martin Preuss
f1c858e3a7 Use n20 for bugfixing. 2025-05-30 15:25:42 +02:00
Martin Preuss
3fc7eff424 add n20. 2025-05-29 22:54:25 +02:00
Martin Preuss
36050b14c5 wait for one bitlength between bytes. disable collision detection. 2025-05-29 21:04:30 +02:00
Martin Preuss
fa6acd8e52 added aqua_n25.xml 2025-05-29 20:27:07 +02:00
Martin Preuss
dbf7f76baa moved versions to a dedicated file shared by all nodes. 2025-05-29 20:26:43 +02:00
Martin Preuss
18be337160 minor change 2025-05-29 20:25:49 +02:00
Martin Preuss
0bd6ef8db4 fix defs for n24. 2025-05-29 20:25:34 +02:00
Martin Preuss
7fb1722c70 added missing .include 2025-05-29 20:25:07 +02:00
Martin Preuss
279d92e338 prepared removal of defs_all.asm 2025-05-29 20:24:41 +02:00
Martin Preuss
a26dd6f2a5 added stats vars to n21 2025-05-29 20:24:13 +02:00
Martin Preuss
785e4ef28c added n16 2025-05-29 20:23:53 +02:00
Martin Preuss
dff347bcb7 created pong message. 2025-05-29 20:23:44 +02:00
Martin Preuss
619ac1564e debug: send rxstats every minute. 2025-05-29 20:20:43 +02:00
Martin Preuss
581eeff996 apps/network: implemented ping request. 2025-05-29 15:47:21 +02:00
Martin Preuss
b4e747c3db added header. 2025-05-29 15:44:30 +02:00
Martin Preuss
04dec73988 added device n25 2025-05-29 15:43:33 +02:00
Martin Preuss
af75532ba7 More planning on GUI. 2025-05-28 19:03:14 +02:00
Martin Preuss
335163f887 temporarily disabled writing text on startup (need the flash space). 2025-05-28 00:52:40 +02:00
Martin Preuss
b3274466a3 smaller changes to make debugging easier. 2025-05-28 00:52:06 +02:00
Martin Preuss
064e84f5e8 added a more compact uart module. 2025-05-28 00:51:37 +02:00
Martin Preuss
18bc231951 added some test code. 2025-05-28 00:51:07 +02:00
Martin Preuss
9a19bf739d fixed a bug (wrong register). 2025-05-28 00:50:52 +02:00
Martin Preuss
9e6feecb88 bootloader: decreased waiting times for LED blinking on bootup. 2025-05-28 00:50:26 +02:00
Martin Preuss
baf77ed182 fixed defs file. 2025-05-28 00:49:44 +02:00
Martin Preuss
b2f7232422 c01: use alternative uart module. 2025-05-28 00:49:28 +02:00
Martin Preuss
961568f721 renamed makros M_IO_READ and M_IO_WRITE to inr and outr 2025-05-28 00:49:07 +02:00
Martin Preuss
042db13994 avr/apps/stats: send VALUE_REPORT messages instead of individual stats messages.
this makes it easier to add some more stats later and it removes some
messages.
2025-05-28 00:47:19 +02:00
Martin Preuss
ba434d88a2 improved output. 2025-05-28 00:45:10 +02:00
Martin Preuss
d32e2f4b81 handle stats values in nodes server. added missing code. 2025-05-28 00:44:58 +02:00
Martin Preuss
e40139fee2 add missing files. 2025-05-26 21:41:41 +02:00
Martin Preuss
d8612a01ca adapted to latest changes. 2025-05-26 21:41:30 +02:00
Martin Preuss
474e63c395 fixed documentation. 2025-05-26 21:40:45 +02:00
Martin Preuss
ceaeb756fb let old file include new files. 2025-05-26 21:40:28 +02:00
Martin Preuss
603a8e93d0 avr/flash: removed unnecessary code. 2025-05-26 21:10:12 +02:00
Martin Preuss
4fdfd77a54 put wait routines into their own files.
helps with reducing size of bootloader.
2025-05-26 21:09:45 +02:00
Martin Preuss
6b5f5e877d avr/heap: fixed some bugs. 2025-05-24 21:01:59 +02:00
Martin Preuss
77bb64fbb6 Merge branch 'mp-2025_05-uart2' 2025-05-24 17:49:44 +02:00
Martin Preuss
92d5e30f3c t03: message forwarding is now much faster.
this is very visible when flashing firmware to nodes via t03 device.
2025-05-24 17:49:01 +02:00
Martin Preuss
349c11d641 reset uart_hw code to what works on t03. 2025-05-24 17:45:33 +02:00
Martin Preuss
027edb9aba removed waiting time between sending messages.
now a message can be send after every interrupt.
2025-05-24 17:26:59 +02:00
Martin Preuss
868c2b32f8 Revert "allow for private tick counter and special timer frequencies."
This reverts commit 3e9aeeed05.
2025-05-24 17:25:24 +02:00
Martin Preuss
2acd25e805 added README. 2025-05-24 17:25:14 +02:00
Martin Preuss
b730fd068c fixed apidoc. 2025-05-24 17:25:00 +02:00
Martin Preuss
3e9aeeed05 allow for private tick counter and special timer frequencies. 2025-05-24 17:24:46 +02:00
Martin Preuss
158cf994c5 fixed a bug on t841. 2025-05-24 17:24:00 +02:00
Martin Preuss
391c06e7e5 avr: more work on fonts, added heap functions. 2025-05-24 15:32:45 +02:00
Martin Preuss
fa5acddcbe avr: started working on prerequisites for a window gui system. 2025-05-22 23:19:33 +02:00
Martin Preuss
7d233136eb moved graphops into a dedicated sourcefile. 2025-05-22 16:29:55 +02:00
Martin Preuss
a4975038b4 more generalisation of font code. 2025-05-22 15:57:03 +02:00
Martin Preuss
229e68077c ili9341: moved text functions to dedicated source file. 2025-05-22 01:19:04 +02:00
Martin Preuss
143180d68b ili9341: added bitblit routines. 2025-05-22 00:42:55 +02:00
Martin Preuss
c065415713 ili9341: created dedicated sourcefile for font handling. 2025-05-22 00:42:18 +02:00
Martin Preuss
b01e8ff004 ili9341: first ideas for window/screen management. 2025-05-21 01:45:02 +02:00
Martin Preuss
de94590fa0 ili9341: moved io code into own file. changed args to r0-r11. 2025-05-21 01:44:38 +02:00
Martin Preuss
b3d7d492d7 ili9341: added routine to write a char in double height/width. 2025-05-21 00:12:38 +02:00
Martin Preuss
d29147fe57 added ili9341_WriteCharacterAt. 2025-05-20 23:36:31 +02:00
Martin Preuss
a0898b8327 insert 2 bytes at beginning of a font (first char, num chars) 2025-05-20 23:36:00 +02:00
Martin Preuss
2ca246deca removed ili9341WriteColorTable 2025-05-20 21:32:53 +02:00
Martin Preuss
c1d347b70b started working with fonts. 2025-05-20 21:32:18 +02:00
Martin Preuss
381afa97f4 ili9341: finalized SPI params. 2025-05-20 21:32:04 +02:00
Martin Preuss
cb379d4149 avr: started working on new LCD module and SPI module. 2025-05-20 00:31:56 +02:00
Martin Preuss
aceffdfad2 uart_hw: removed unneeded code. 2025-05-20 00:31:12 +02:00
Martin Preuss
8ece026f2d Add wait routines for milliseconds. 2025-05-20 00:29:45 +02:00
Martin Preuss
d000c8621e increased buffer size. 2025-05-18 01:04:15 +02:00
Martin Preuss
cf1eba68f0 try new implementation of UART code for m8515. 2025-05-18 00:59:19 +02:00
Martin Preuss
b6800e538d tried some fixes... 2025-05-18 00:58:46 +02:00
Martin Preuss
b8fbbb7125 remove COM_* code. 2025-05-18 00:58:11 +02:00
Martin Preuss
4b4753415a sort order of stats messages. 2025-05-18 00:57:03 +02:00
Martin Preuss
1c053d0492 fixed apidoc. 2025-05-18 00:56:43 +02:00
Martin Preuss
44b8848996 minor beautifications. 2025-05-17 14:24:23 +02:00
Martin Preuss
550967c176 memstats: send XRAM size instead of stack size. 2025-05-17 14:24:04 +02:00
Martin Preuss
e713711a61 added missing includes. 2025-05-17 14:23:34 +02:00
Martin Preuss
613a7e629d remove unneeded includes. 2025-05-17 14:23:24 +02:00
Martin Preuss
5026a7b753 c01: use modules CLOCK and XRAM. 2025-05-17 14:23:10 +02:00
Martin Preuss
75b602811c added XRAM module.
Module for external SRAM with AtMega8515.
2025-05-17 14:22:49 +02:00
Martin Preuss
2897aece2c added memory stats. 2025-05-17 14:22:05 +02:00
Martin Preuss
2cc260790c c01: removed unneeded code. 2025-05-17 13:21:07 +02:00
Martin Preuss
1ac4b8cad0 c01: enable more modules and apps. 2025-05-17 13:20:51 +02:00
Martin Preuss
ab3b2be725 consequently use M_IO_READ and M_IO_WRITE.
on tn841 those are in the extended io space, on most others in normal
io space.
2025-05-17 13:19:54 +02:00
Martin Preuss
10e4aa8f85 fixed a problem with m8515 UART.
UBRRH and UCSRC share the same io register location, bit URSEL needs to
be set in order to access UCSRC!!
2025-05-17 13:18:50 +02:00
Martin Preuss
ea0d426430 added definition for bit USBS 2025-05-17 13:17:16 +02:00
Martin Preuss
27a0214b5d removed old COM2 parts from code. 2025-05-17 13:16:46 +02:00
Martin Preuss
f9cd1833da Merge branch 'master' into mp-2025_05-mega8515 2025-05-17 10:51:54 +02:00
Martin Preuss
f8d3d09716 n21: fixed README. 2025-05-17 10:51:21 +02:00
Martin Preuss
484a455df7 t03: use new uart file. 2025-05-17 10:51:04 +02:00
Martin Preuss
21c2c60f4f started working on AtMega8515 module C1. 2025-05-17 10:50:09 +02:00
Martin Preuss
5a46db37c1 bootloader: decreased waiting time. 2025-05-17 10:49:03 +02:00
Martin Preuss
e545251b27 moved timer setup code into hardware include file. 2025-05-17 10:48:39 +02:00
Martin Preuss
d51ca9b1d1 n21: added VALUE_ID_LEDSIMPLE_TIMING 2025-05-17 10:47:59 +02:00
Martin Preuss
dba503df5b only call LedSimple_OnPacketReceived is networking code enabled. 2025-05-17 10:47:44 +02:00
Martin Preuss
732dcd74f7 only include "sendvalue.asm" if networking code enabled. 2025-05-17 10:47:29 +02:00
Martin Preuss
930229b969 add macros for reading/writing IO regs regardless of their position (i.e. < or >=0x40). 2025-05-17 10:46:46 +02:00
Martin Preuss
bb4e16cae7 add line with ATTN. 2025-05-17 10:46:03 +02:00
Martin Preuss
f5606554c9 added tool flashnode.sh 2025-05-17 10:45:37 +02:00
Martin Preuss
d97d2ac08f fixed a bug. 2025-05-17 10:44:48 +02:00
Martin Preuss
f834a4ecc5 fixed apidoc. 2025-05-17 10:44:37 +02:00
Martin Preuss
92b7d1bb13 valsched: only repeat values in first loop. 2025-05-15 00:58:54 +02:00
Martin Preuss
b775c9b6b2 n23/n24: improved handling of some values. Removed old files. 2025-05-15 00:34:57 +02:00
Martin Preuss
63acb95834 started working on node n24. 2025-05-14 23:20:16 +02:00
Martin Preuss
faa46c7e38 added modules for SGP30/40. 2025-05-14 23:18:26 +02:00
Martin Preuss
fee3606a61 added "LEDTIMING" value. 2025-05-14 01:51:17 +02:00
Martin Preuss
92bcd366cb add functions to remotely set LED timing. 2025-05-14 01:50:59 +02:00
Martin Preuss
bc54f5bda1 fixed a bug. 2025-05-14 01:50:34 +02:00
Martin Preuss
30945807d8 minor changes. 2025-05-14 01:50:23 +02:00
Martin Preuss
95a0e6ea9d Reserve bytes in EEPROM. 2025-05-14 01:49:42 +02:00
Martin Preuss
47275556f9 allow for remotely setting LED timing. 2025-05-14 01:49:23 +02:00
Martin Preuss
25fb6f262d fixed a bug. 2025-05-14 01:48:40 +02:00
Martin Preuss
147f920eb6 avr/sk6812: improved protocol implementation.
still doesn't work with new 5m LED strips from BTF-Lighting (1m strips
do work, as do older 5m stripes).
2025-05-14 01:48:24 +02:00
Martin Preuss
b63d625d12 Log all messages. 2025-05-14 01:46:43 +02:00
Martin Preuss
11657cef75 added missing file. 2025-05-10 15:18:43 +02:00
Martin Preuss
5dd0e6fd95 add "-N" to aqhome-nodes.service 2025-05-10 15:17:57 +02:00
Martin Preuss
9a993445c4 adapted n16 to latest changes. 2025-05-10 15:17:35 +02:00
Martin Preuss
4baa949a77 moved multiplication function to "common" dir, added divide routine. 2025-05-10 15:17:08 +02:00
Martin Preuss
c7823d3874 added "-N" 2025-05-10 15:15:05 +02:00
Martin Preuss
074a7e5650 cleanup build system. 2025-05-08 17:59:27 +02:00
Martin Preuss
64e2cf5d25 avr/devices/all: started integrating code from t03. 2025-05-08 17:14:33 +02:00
Martin Preuss
cc7ef0cf30 flash: wait for ATTN high before waiting for message.
this helps with syncing.
2025-05-07 20:03:58 +02:00
Martin Preuss
9102d90c34 flash: minor timing change. 2025-05-07 20:03:06 +02:00
Martin Preuss
6832487c46 flash: fixed a bug (reversed register order). 2025-05-07 20:02:42 +02:00
Martin Preuss
f5cf85a931 flash: reduced waiting times. 2025-05-07 20:02:07 +02:00
Martin Preuss
d57d721dbf flash: increased number of retries when waiting for FLASH_DATA msg. 2025-05-07 20:01:34 +02:00
Martin Preuss
aa97197413 added missing include. 2025-05-07 20:00:58 +02:00
Martin Preuss
d7df84549b Fixed a typo. 2025-05-07 20:00:42 +02:00
Martin Preuss
8feb8982c1 added README. 2025-05-07 20:00:27 +02:00
Martin Preuss
f4acc829f7 avr/devices/n21: removed unneeded definitions. 2025-05-05 21:06:07 +02:00
Martin Preuss
34b2fe4007 avr/devices/n23: add missing include. 2025-05-05 21:05:49 +02:00
Martin Preuss
72f52da17b avr/modules/sk6812: uncomment unused code. 2025-05-05 21:05:30 +02:00
Martin Preuss
eecb022a0c avr/modules/flash: make protocol more robust
- wait for ATTN high after error receiving a packet
- use different wait times when waiting for FLASH_START and FLASH_DATA
2025-05-05 21:05:07 +02:00
Martin Preuss
9063962c7d avr/apps/ma_light: adapted to latest changes. works now. 2025-05-05 21:03:13 +02:00
Martin Preuss
9e25cbb183 apps/aqhome-tool/flash: shorten re-send time 2025-05-05 21:02:17 +02:00
Martin Preuss
9af57eaac0 sk6812: don't pull DIN high (sometimes modifies first LED). 2025-05-05 01:20:30 +02:00
Martin Preuss
f31567e785 sk6812: send RESET to led strip at start. 2025-05-04 22:42:42 +02:00
Martin Preuss
dc62f1fe05 added mising file. 2025-05-04 22:42:11 +02:00
Martin Preuss
868268d116 t03: adapted to latest changes. 2025-05-04 03:38:41 +02:00
Martin Preuss
9f663b0ed9 n21, n23: increase buffer size to 32 (otherwise value messages might not fit). 2025-05-04 03:38:09 +02:00
Martin Preuss
827497bb5e minor format change. 2025-05-04 03:37:22 +02:00
Martin Preuss
688f02df47 re-add n22. 2025-05-04 03:36:56 +02:00
Martin Preuss
33ec6d0a67 fixed apidoc. 2025-05-04 03:36:13 +02:00
Martin Preuss
b3f19b0432 add additional waitstate after acquiting bus. 2025-05-04 03:35:59 +02:00
Martin Preuss
e6ea506a74 sk6812 now works again (at least with 1m LEDs). 2025-05-04 03:35:28 +02:00
Martin Preuss
2ac090cf14 some fixes. 2025-05-04 03:34:48 +02:00
Martin Preuss
46b24bf442 avr/modules/network: disable interrupts on alloc/release. 2025-05-04 03:34:11 +02:00
Martin Preuss
9a854977a4 avr: more work on basic device code. 2025-05-04 03:33:32 +02:00
Martin Preuss
e25b0ad69d avr/module/flash: unified flash code for 1p/4p devices.
AtTiny 841 erases 4 pages at once, others only erase one.
Fixed a bug: only send ONE flash response to FLASH_START!!!
2025-05-04 03:32:53 +02:00
Martin Preuss
b632a10fff avr/apps: added ma_light (was previously a module). 2025-05-04 03:30:55 +02:00
Martin Preuss
e20b1c52d3 increased verbosity. 2025-05-04 03:30:28 +02:00
Martin Preuss
08e1428902 avr: adapted n23 to latest changes. 2025-05-01 01:01:22 +02:00
Martin Preuss
5f220dc86f avr: adapted n21 to latest changes. 2025-05-01 01:01:05 +02:00
Martin Preuss
51d32dc505 avr: move SK6812_OnPacketReceived to dedicated file. 2025-05-01 00:59:02 +02:00
Martin Preuss
048eccf4c6 avr: toggle LED while waiting for ATTN line. 2025-05-01 00:58:32 +02:00
Martin Preuss
9e37ebb261 avr: adapted to latest changes. 2025-05-01 00:58:14 +02:00
Martin Preuss
de89bc94b6 avr: use 2 byte counter for f_keepup. 2025-05-01 00:57:48 +02:00
Martin Preuss
ab9c177793 avr: adapted ds18b20 module to latest changes. 2025-05-01 00:57:22 +02:00
Martin Preuss
6ce1aac1d7 avr: rename routines to call internal ones instead of directly calling into the main app. 2025-05-01 00:56:40 +02:00
Martin Preuss
e56f2600e8 avr: more work on avr app framework. 2025-05-01 00:56:03 +02:00
Martin Preuss
614a30fd4e avr: generate new uid if the current one consists of 4 equal bytes. 2025-05-01 00:55:33 +02:00
Martin Preuss
5981ec44e4 avr: only write COM address if changed to reduce EEPROM wear. 2025-05-01 00:54:48 +02:00
Martin Preuss
8008819989 avr: added apps and missing files. 2025-05-01 00:53:29 +02:00
Martin Preuss
7edaed601b fixed a typo. 2025-05-01 00:52:48 +02:00
Martin Preuss
06434512c7 avr: added new version of tcrt1000. 2025-04-29 00:37:31 +02:00
Martin Preuss
fa65bc53b5 avr: added new version of driver for si7021. 2025-04-29 00:37:01 +02:00
Martin Preuss
c1a67a36ef avr/modules/network: added messages. 2025-04-29 00:36:20 +02:00
Martin Preuss
1e90682605 avr: use new code in latest nodes. 2025-04-29 00:35:45 +02:00
Martin Preuss
2357b63b42 added initial wait routine. 2025-04-29 00:34:53 +02:00
Martin Preuss
e1b2650221 avr: added apps and use them in multiple nodes. 2025-04-29 00:34:24 +02:00
Martin Preuss
52ae473ad1 aqhome-tool: no longer expect a reboot response. 2025-04-29 00:32:59 +02:00
Martin Preuss
833819f713 network: write new address into EEPROM. 2025-04-26 11:10:23 +02:00
Martin Preuss
155fc9a743 uart_bitbang2: use new constants. 2025-04-26 11:07:31 +02:00
Martin Preuss
a03fc012f4 uart_bitbang2: hardcoded interface data. 2025-04-26 11:07:15 +02:00
Martin Preuss
5b93d58309 tcrt1000: started simplified version of driver.
message sending/timing/signal shaping is now done externally in the filter
modules so that they can be reused by multiple drivers.
2025-04-26 11:06:25 +02:00
Martin Preuss
bef677eda4 valsched: use cbr and sbr. 2025-04-26 11:05:27 +02:00
Martin Preuss
265a1e6043 si7021: started simplifyied version of driver. 2025-04-26 11:05:07 +02:00
Martin Preuss
b21c111aa3 network: fixed vars. 2025-04-26 11:04:41 +02:00
Martin Preuss
b9419520f4 network: removed duplicate code. 2025-04-26 11:04:28 +02:00
Martin Preuss
8925ce23e9 N23 basically works now. 2025-04-26 11:04:01 +02:00
Martin Preuss
feabc76e36 started working on new n23 code. 2025-04-21 00:47:27 +02:00
Martin Preuss
f806cf30e5 Revert "added ifdefs guards for includes."
This reverts commit 15199a17a5.
2025-04-21 00:46:57 +02:00
Martin Preuss
15199a17a5 added ifdefs guards for includes. 2025-04-21 00:46:46 +02:00
Martin Preuss
30654509b1 added uart_bitbang2. 2025-04-21 00:45:59 +02:00
Martin Preuss
ea2162e8f4 added filter. 2025-04-21 00:44:04 +02:00
Martin Preuss
ebba98ef16 si7021: honour PRESENT flag, added GetValue 2025-04-21 00:43:50 +02:00
Martin Preuss
b995f1c3fb added more messages. 2025-04-21 00:43:11 +02:00
Martin Preuss
77e573192e more work on network interface. 2025-04-21 00:39:19 +02:00
Martin Preuss
ba55a2898d started working on 2nd version of motion module. 2025-04-21 00:36:45 +02:00
Martin Preuss
16a6bb08b5 fixed return values. 2025-04-21 00:36:07 +02:00
Martin Preuss
b2a087c959 added debug code. 2025-04-21 00:33:52 +02:00
Martin Preuss
7349dcba8d flash: fixed wait times etc. 2025-04-21 00:32:59 +02:00
Martin Preuss
e4145afbfe added filter modules. 2025-04-21 00:32:30 +02:00
Martin Preuss
876d1cad0e added module "clock" 2025-04-21 00:32:00 +02:00
Martin Preuss
eea0a66b84 aqhome-react: fixed unit timeprogram. 2025-04-21 00:29:53 +02:00
Martin Preuss
b4d71a0fad added app aqhome-cgi. 2025-04-21 00:29:31 +02:00
Martin Preuss
89542e06c9 avr: consolidated uart_hw module 2025-03-27 00:20:58 +01:00
Martin Preuss
3555b49219 avr: added device n20 back in. 2025-03-25 00:39:44 +01:00
Martin Preuss
791afb80f8 avr: added macros M_UART_HW_Uart_RxCharHalf/FullDuplexIsr 2025-03-25 00:39:28 +01:00
Martin Preuss
924c4d27e8 avr: added network/msg/crc.asm 2025-03-25 00:38:42 +01:00
Martin Preuss
cac611deae avr: use NETMSG functions, increase waiting times. 2025-03-25 00:38:12 +01:00
Martin Preuss
aee2c384c8 avr: fixed includes. 2025-03-25 00:37:12 +01:00
Martin Preuss
78fda7727a avr: added some comments. 2025-03-24 00:18:50 +01:00
Martin Preuss
45e668ec88 aqhome-tool: flash always 16 bytes at a time 2025-03-24 00:18:28 +01:00
Martin Preuss
c6f1ac94fa increased version number. 2025-03-23 22:41:30 +01:00
Martin Preuss
8c9fd4574d t03: debug; decrease time between stats messages. 2025-03-23 22:41:20 +01:00
Martin Preuss
f9a73a61e9 n14: added debug code. 2025-03-23 22:40:52 +01:00
Martin Preuss
0165b02310 flashing older nodes across t03 now works!
older nodes need the new bootloader which doesn't depend on pagesize because
those older nodes used quite large message sizes which are not supported
across t03 nodes (we would need to much RAM on t03 for this).
2025-03-23 22:40:34 +01:00
Martin Preuss
24dc3e1c0a aqhome: fixed flashing messages and code
use consisten order of arguments.
2025-03-23 22:38:37 +01:00
Martin Preuss
7b6cb4da34 aqhome-tool: decreased verbosity. 2025-03-23 22:37:55 +01:00
Martin Preuss
c8fcd46325 aqhome-data: changed return type. 2025-03-23 22:37:34 +01:00
Martin Preuss
3ffc4a3410 fixed/added valgrind caller scripts. 2025-03-23 16:58:23 +01:00
Martin Preuss
52eb92a00c fixed memory leaks. 2025-03-23 16:58:07 +01:00
Martin Preuss
ebce002614 avr: share code. 2025-03-23 01:08:33 +01:00
Martin Preuss
595f983071 avr/uart_hw, t03: cross messaging now works!! 2025-03-22 23:44:15 +01:00
Martin Preuss
8586269033 avr/t03: more tests. 2025-03-22 13:00:35 +01:00
Martin Preuss
0f598a5552 avr/uart_hw: tty1 works in send and receive direction. 2025-03-22 13:00:20 +01:00
Martin Preuss
236f6832a3 avr: fixed a critical bug (don't decrement used bytes on peek!) 2025-03-22 12:59:35 +01:00
Martin Preuss
373a553ac8 avr/uart_hw: basically works for a while then stops... 2025-03-21 20:55:50 +01:00
Martin Preuss
2964347e7b adjusted to latest changes. 2025-03-21 20:55:11 +01:00
Martin Preuss
3f0e9c9294 increased verbosity. 2025-03-21 20:54:57 +01:00
Martin Preuss
0b946c5748 aqhome-tool: fixed a bug. 2025-03-21 20:54:41 +01:00
Martin Preuss
c808f8640e aqhome-nodes: allow for TTY interface without ATTN line. 2025-03-21 20:54:19 +01:00
Martin Preuss
1fa0d976d2 increased version. 2025-03-19 23:22:59 +01:00
Martin Preuss
091307290e aqhome-react: fixed timeprogram module. 2025-03-19 23:22:46 +01:00
Martin Preuss
7a045abcc2 scripts: use "adddata" instead of "addjsondata". 2025-03-19 23:22:26 +01:00
Martin Preuss
95d589b126 aqhome-react: fixed argument checks. 2025-03-19 23:21:55 +01:00
Martin Preuss
d27664d7c9 aqhome-data: no tagList needed here. 2025-03-19 23:21:31 +01:00
Martin Preuss
ebf9c6eb30 aqhome-tool: fixed return value ("1" stops the calling loop). 2025-03-19 23:21:03 +01:00
Martin Preuss
fef6011426 aqhome-react: show values sent. 2025-03-19 23:20:23 +01:00
Martin Preuss
daa07428eb aqhome apps: decreased verbosity. 2025-03-19 23:19:52 +01:00
Martin Preuss
0d8d684e78 prepared release 0.0.6. 2025-03-18 22:58:39 +01:00
Martin Preuss
bab6721b2c aqhome-react.sh: fixed order of arguments (enables "test" and "unittest"). 2025-03-18 22:58:27 +01:00
Martin Preuss
d468d2fb1a aqhome-tool: exchange CONNECT messages for data IPC. 2025-03-18 22:57:51 +01:00
Martin Preuss
d897f40903 aqhome-react: add missing includes. 2025-03-18 22:56:42 +01:00
Martin Preuss
9a7a7e75f9 aqhome-react: enable unit tests. 2025-03-18 22:56:10 +01:00
Martin Preuss
a14c2c892c libtest: no longer include old files. 2025-03-18 22:55:32 +01:00
Martin Preuss
cc64f793ec build: add missing files. 2025-03-18 22:55:08 +01:00
Martin Preuss
32d5287a3a aqhome-mqttlog: unify arguments. 2025-03-18 22:54:34 +01:00
Martin Preuss
4dd3decb06 aqhome-nodes: fixed a bug (reversed src and dest addresses). 2025-03-18 22:54:11 +01:00
Martin Preuss
171caacc92 aqhome-apps: no longer included removed files. 2025-03-18 22:53:34 +01:00
Martin Preuss
2320ba3a69 aqhome-data: fixed a bug (don't need tagList for "GetValues"). 2025-03-18 22:51:49 +01:00
Martin Preuss
72c8923bfe avr: more work on uart_hw. 2025-03-14 23:29:43 +01:00
Martin Preuss
14c9ac17a1 avr: disable module for n11. 2025-03-14 21:30:51 +01:00
Martin Preuss
7e745c98e6 aqhome, aqhome-apps: cleanup, removed unneeded files. 2025-03-14 21:22:48 +01:00
Martin Preuss
31d8cb10df Merge branch 'mp-2025_02-ipc2_event2'
Pull latest changes as current state into master branch.
2025-03-11 23:55:31 +01:00
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
1127 changed files with 121250 additions and 12368 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.*

83
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.22" so_current="0" so_age="0" so_revision="22" write_config_h="TRUE">
<setVar name="package">$(project_name)</setVar>
<setVar name="version">
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
@@ -44,23 +44,83 @@
<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>
<setVar name="httpdatadir">/var/www</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>
@@ -68,6 +128,7 @@
</option>
<checkheaders>
signal.h
sys/stat.h
@@ -88,9 +149,15 @@
<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="aqcgi" name="aqcgi" minversion="0.0.1" required="TRUE" />
<dep id="aqdiagram" name="aqdiagram" minversion="0.0.1" required="TRUE" />
<!-- <dep id="aqdatabase" name="aqdatabase" minversion="1.99.1" required="TRUE" >
<variables>aqdatabase_typemakerdir</variables>
</dep>
-->
</dependencies>
@@ -110,6 +177,9 @@
<subdirs>
aqhome
apps
devices
etc
scripts
</subdirs>
<ifVarMatches name="option_with_avr" value="TRUE" >
@@ -131,7 +201,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,12 @@
<gwbuild>
<subdirs>
aqhomed
aqhome-tool
aqhome-mqttlog
aqhome-data
aqhome-nodes
aqhome-react
aqhome-cgi
</subdirs>
</gwbuild>

150
apps/aqhome-cgi/0BUILD Normal file
View File

@@ -0,0 +1,150 @@
<?xml?>
<gwbuild>
<target type="Program" name="aqhome-cgi" install="$(libdir)/cgi-bin" >
<includes type="c" >
$(gwenhywfar_cflags)
$(aqcgi_cflags)
$(aqdiagram_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<define name="AQHOME_CGI_WWWDIR" value="$(httpdatadir)/aqhome-cgi" quoted="TRUE" />
<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>
aqhomecgi
aqhcgi_service
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
-lm
$(aqcgi_libs)
$(aqdiagram_libs)
</libraries>
<subdirs>
</subdirs>
<extradist>
</extradist>
</target>
<target type="ConvenienceLibrary" name="aqhomecgi" >
<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" >
service_file.h
service_file_p.h
</headers>
<sources>
$(local/typefiles)
service_file.c
</sources>
<useTargets>
aqhome
aqhcgi_service
aqhcgi_modules
</useTargets>
<libraries>
$(gwenhywfar_libs)
-lm
$(aqcgi_libs)
$(aqdiagram_libs)
</libraries>
<subdirs>
service
modules
</subdirs>
<extradist>
</extradist>
</target>
</gwbuild>

23
apps/aqhome-cgi/README Normal file
View File

@@ -0,0 +1,23 @@
Modules:
- login
- signup
- main
- devices
- list
- show?name="nodes/12345678"
- add
- edit
- delete
- values
- rooms
- users
- list
- show?alias="admin"
- add
- edit
- delete
- dashboards

0
apps/aqhome-cgi/dummy.c Normal file
View File

264
apps/aqhome-cgi/main.c Normal file
View File

@@ -0,0 +1,264 @@
#include "./service_file.h"
#include "aqhome-cgi/modules/mroot.h"
#include "aqhome-cgi/modules/common/madmin.h"
#include "aqhome-cgi/modules/common/mmodules.h"
#include "aqhome-cgi/modules/common/musers.h"
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include <aqcgi/cgi.h>
#include <aqcgi/request.h>
#include <aqdiagram/aqdiagram.h>
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/nogui.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#define AQHOME_CGI_LOGFILE "/var/www/aqhome-cgi/log/aqhome-cgi.log"
#define AQHOME_CGI_DEFAULT_STATIC_FILES AQHOME_CGI_WWWDIR"/static"
#define AQHOME_CGI_DEFAULT_RUNTIME_FILES AQHOME_CGI_WWWDIR"/data"
#define AQHOME_CGI_DEFAULT_CACHE_FILES AQHOME_CGI_WWWDIR"/cache"
#define AQHOME_CGI_DEFAULT_BASE_URL "http://127.0.0.1/aqbt"
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles, const char *sBaseUrl);
static int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles);
static void logStart(void);
static int _init(const char *sPathRuntimeFiles, const char *sBaseUrl);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int main(int argc, char **argv)
{
GWEN_GUI *gui;
const char *sPathStaticFiles;
const char *sPathRuntimeFiles;
const char *sBaseUrl;
sPathStaticFiles=getenv("AQHOME_STATIC_FILES");
if (!(sPathStaticFiles && *sPathStaticFiles))
sPathStaticFiles=AQHOME_CGI_DEFAULT_STATIC_FILES;
sPathRuntimeFiles=getenv("AQHOME_RUNTIME_FILES");
if (!(sPathRuntimeFiles && *sPathRuntimeFiles))
sPathRuntimeFiles=AQHOME_CGI_DEFAULT_RUNTIME_FILES;
sBaseUrl=getenv("AQHOME_BASE_URL");
if (!(sBaseUrl && *sBaseUrl))
sBaseUrl=AQHOME_CGI_DEFAULT_BASE_URL;
GWEN_Init();
gui=GWEN_NoGui_new();
GWEN_Gui_SetGui(gui);
logStart();
GWEN_Logger_Open(GWEN_LOGDOMAIN, "gwenhywfar", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(AQH_LOGDOMAIN, "aqhome", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(AQCGI_LOGDOMAIN, "aqcgi", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(AQDG_LOGDOMAIN, "aqdiagram", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(NULL, "aqhome-cgi", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(AQCGI_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Debug);
GWEN_Logger_Close(GWEN_LOGDOMAIN);
GWEN_Logger_Open(GWEN_LOGDOMAIN, "gwenhywfar", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(AQCGI_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(AQDG_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Debug);
if (argc>1 && argv[1]) {
if (0==strcasecmp(argv[1], "init")) {
int rv;
rv=_init(sPathRuntimeFiles, sBaseUrl);
if (rv<0) {
fprintf(stderr, "Error on init (%d)\n", rv);
return 2;
}
}
}
else {
AQCGI_REQUEST *rq;
DBG_ERROR(NULL, "Init CGI");
AQCGI_Init();
rq=AQCGI_ReadRequest();
if (rq) {
_handleRequest(rq, sPathStaticFiles, sPathRuntimeFiles, sBaseUrl);
}
else {
fprintf(stdout, "Content-type: text/plain\n\n");
fprintf(stdout, "Error: No Request!\n");
}
AQCGI_Fini();
}
return 0;
}
void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles, const char *sBaseUrl)
{
AQH_SERVICE *sv;
int rv;
sv=AQH_ServiceFiles_new(sPathRuntimeFiles, sBaseUrl);
rv=_handlePath(sv, rq, sPathStaticFiles);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Service_free(sv);
}
int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles)
{
AQH_MODULE *mRoot;
AQH_MODULE *mParent;
AQH_SESSION *session;
const GWEN_STRINGLIST *sl;
mRoot=AQH_ModRoot_new(sv, sPathStaticFiles);
mParent=mRoot;
session=AQH_ModService_ReadSession(mRoot, rq);
AQH_ModService_CalcSessionModPerms(mRoot, session);
sl=AQCGI_Request_GetStringlistPath(rq);
if (sl) {
GWEN_STRINGLISTENTRY *se;
se=GWEN_StringList_FirstEntry(sl);
while(se) {
GWEN_STRINGLISTENTRY *seNext;
const char *s;
seNext=GWEN_StringListEntry_Next(se);
s=GWEN_StringListEntry_Data(se);
if (s && *s) {
if (seNext) {
AQH_MODULE *m;
DBG_ERROR(NULL, "Entry: %s (%s)", s, seNext?"not last":"last");
m=AQH_ModService_LoadSubModule(mParent, rq, session, s);
if (m==NULL) {
AQH_Session_free(session);
AQH_Module_free(mRoot);
return GWEN_ERROR_GENERIC;
}
mParent=m;
}
else {
int rv;
/* last, let module handle remaining part */
DBG_ERROR(NULL, "Entry: %s (last)", s);
rv=AQH_ModService_HandleRequest(mParent, rq, session, s);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQH_Session_free(session);
AQH_Module_free(mRoot);
return rv;
}
break;
}
}
se=seNext;
}
AQH_Session_free(session);
AQH_Module_free(mRoot);
return 0;
}
else {
AQH_Session_free(session);
AQH_Module_free(mRoot);
return GWEN_ERROR_GENERIC;
}
}
void logStart()
{
FILE *f;
f=fopen(AQHOME_CGI_LOGFILE, "a+");
if (f!=NULL) {
fprintf(f, "Started.\n");
fclose(f);
}
}
int _init(const char *sPathRuntimeFiles, const char *sBaseUrl)
{
AQH_SERVICE *sv;
int rv;
DBG_ERROR(NULL, "Creating aqhome-cgi environment in \"%s\"", sPathRuntimeFiles);
sv=AQH_ServiceFiles_new(sPathRuntimeFiles, sBaseUrl);
rv=AQH_ModAdmin_Create(sv);
if (rv<0) {
DBG_ERROR(NULL, "Error creating module \"admin\"");
AQH_Service_free(sv);
return rv;
}
rv=AQH_ModAdmModules_Create(sv);
if (rv<0) {
DBG_ERROR(NULL, "Error creating module \"modules\"");
AQH_Service_free(sv);
return rv;
}
rv=AQH_ModAdmUsers_Create(sv);
if (rv<0) {
DBG_ERROR(NULL, "Error creating module \"users\"");
AQH_Service_free(sv);
return rv;
}
rv=AQH_ModDevices_Create(sv);
if (rv<0) {
DBG_ERROR(NULL, "Error creating module \"devices\"");
AQH_Service_free(sv);
return rv;
}
AQH_Service_free(sv);
return 0;
}

View File

@@ -0,0 +1,87 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhcgi_modules" >
<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>
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags-INACTIVE" >
--api=AQHOME_API
</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="false" install="$(pkgincludedir)/service" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/service" >
mdataclient.h
mroot.h
</headers>
<headers dist="true" >
mroot_p.h
</headers>
<sources>
$(local/typefiles)
mdataclient.c
mroot.c
</sources>
<extradist>
</extradist>
<useTargets>
aqhcgi_modcom
aqhcgi_mdevices
</useTargets>
<subdirs>
common
devices
static
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,86 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhcgi_modcom" >
<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>
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags-INACTIVE" >
--api=AQHOME_API
</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="false" install="$(pkgincludedir)/service" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/service" >
mservice.h
madmin.h
mmodules.h
musers.h
</headers>
<headers dist="true" >
mservice_p.h
</headers>
<sources>
$(local/typefiles)
mservice.c
madmin.c
mmodules.c
musers.c
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,217 @@
/****************************************************************************
* 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 "./madmin.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/common/mmodules.h"
#include "aqhome-cgi/modules/common/musers.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _createPermDefList(AQH_MODULE *m);
static void _createRoleList(AQH_MODULE *m);
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModAdmin_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
}
int AQH_ModAdmin_Create(AQH_SERVICE *sv)
{
AQH_MODULE *m;
int rv;
m=AQH_Module_new();
AQH_Module_SetName(m, "admin");
AQH_Module_SetDescr(m, "administration module");
AQH_Module_SetGuestPerms(m, 0);
_createPermDefList(m);
_createRoleList(m);
rv=AQH_Service_AddModule(sv, m);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Module_free(m);
return rv;
}
void _createPermDefList(AQH_MODULE *m)
{
AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_PermDef_List_new();
AQH_ModService_AddPermDef(permDefList, "AdminUsers", 0x001, "User Administration");
AQH_ModService_AddPermDef(permDefList, "AdminModules", 0x002, "Module Administration");
AQH_Module_SetPermDefList(m, permDefList);
}
void _createRoleList(AQH_MODULE *m)
{
AQH_ROLE_LIST *roleList;
int id=0;
roleList=AQH_Role_List_new();
AQH_ModService_AddRole(roleList, id++, "userAdmin", AQH_MODADM_PERMS_ADMINUSERS, "User administrator");
AQH_ModService_AddRole(roleList, id++, "moduleAdmin", AQH_MODADM_PERMS_ADMINMODULES, "Module administrator");
AQH_Module_SetRoleList(m, roleList);
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
AQH_SERVICE *sv;
sv=AQH_ModService_GetService(m);
if (strcasecmp(sModuleName, "modules")==0) {
AQH_MODULE *mSub;
mSub=AQH_Service_LoadModule(sv, sModuleName);
if (mSub) {
const char *s;
GWEN_BUFFER *nbuf;
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
s=AQH_ModService_GetBaseFolder(m);
GWEN_Buffer_AppendArgs(nbuf, "%s/modules", s?s:".");
AQH_ModAdmModules_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
AQH_Module_Tree2_AddChild(m, mSub);
GWEN_Buffer_free(nbuf);
return mSub;
}
}
else if (strcasecmp(sModuleName, "users")==0) {
AQH_MODULE *mSub;
mSub=AQH_Service_LoadModule(sv, sModuleName);
if (mSub) {
const char *s;
GWEN_BUFFER *nbuf;
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
s=AQH_ModService_GetBaseFolder(m);
GWEN_Buffer_AppendArgs(nbuf, "%s/modules", s?s:".");
AQH_ModAdmUsers_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
AQH_Module_Tree2_AddChild(m, mSub);
GWEN_Buffer_free(nbuf);
return mSub;
}
}
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
GWEN_BUFFER *dbuf;
int rv=0;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_ModService_AddHeader(m, "en", dbuf);
if (strcasecmp(sLastPathElem, "index.html")==0) {
if (AQH_ModService_GetUserPerms(m) & (AQH_MODADM_PERMS_ADMINUSERS | AQH_MODADM_PERMS_ADMINMODULES))
rv=_handleRqIndex(m, rq, dbuf);
else {
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
}
}
else {
AQCGI_Request_SetResponseCode(rq, 404);
AQCGI_Request_SetResponseText(rq, "Not Found");
}
AQH_ModService_AddFooter(m, "en", dbuf);
AQCGI_Request_SetBufferResponseBody(rq, dbuf);
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
return AQCGI_SendResponse(rq);
}
int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET) {
uint32_t userPerms;
GWEN_Buffer_AppendString(dbuf, "<table>");
userPerms=AQH_ModService_GetUserPerms(m);
if (userPerms & AQH_MODADM_PERMS_ADMINUSERS)
GWEN_Buffer_AppendString(dbuf,
"<tr>"
"<td><a href=\"users/index.html\" >User administration</a></td>"
"<td>Add, remove or modify users</td>"
"</tr>\n");
if (userPerms & AQH_MODADM_PERMS_ADMINMODULES)
GWEN_Buffer_AppendString(dbuf,
"<tr>"
"<td><a href=\"modules/index.html\" >Module administration</a></td>"
"<td>Add, remove or modify modules</td>"
"</tr>\n");
GWEN_Buffer_AppendString(dbuf, "</table>\n");
AQCGI_Request_SetResponseCode(rq, 200);
AQCGI_Request_SetResponseText(rq, "Ok");
return 0;
}
AQCGI_Request_SetResponseCode(rq, 405);
AQCGI_Request_SetResponseText(rq, "Method Not Allowed");
return 0;
}

View File

@@ -0,0 +1,31 @@
/****************************************************************************
* 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_CGI_MADMIN_H
#define AQHOME_CGI_MADMIN_H
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
#define AQH_MODADM_PERMS_ADMINUSERS 0x001
#define AQH_MODADM_PERMS_ADMINMODULES 0x002
void AQH_ModAdmin_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
int AQH_ModAdmin_Create(AQH_SERVICE *sv);
#endif

View File

@@ -0,0 +1,745 @@
/****************************************************************************
* 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 "./mmodules.h"
#include "aqhome-cgi/service/module.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _createPermDefList(AQH_MODULE *m);
static void _createRoleList(AQH_MODULE *m);
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static void _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditModGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditModPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqAddRoleGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqAddRolePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditRoleGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditRolePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static int _getHighestUsedRoleId(const AQH_ROLE_LIST *roleList);
static void _handleRqDeleteRole(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _writeEditModForm(const AQH_MODULE *currentMod, const char *sModName, GWEN_BUFFER *dbuf);
static void _writeRoleListToForm(const AQH_ROLE_LIST *roleList,
const char *sModName,
const AQH_PERMDEF_LIST *permDefList,
GWEN_BUFFER *dbuf);
static void _setLocationHeaderForMod(AQCGI_REQUEST *rq, const char *page, const char *sModName);
static void _writeEnabledPermissions(const AQH_PERMDEF_LIST *permDefList, uint32_t perms, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* vars
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
{"index.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMMODULES_PERMS_MODULESREAD, _handleRqIndex},
{"editmodule.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqEditModGet},
{"editmodule.html", AQCGI_REQUEST_METHOD_POST, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqEditModPost},
{"addrole.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqAddRoleGet},
{"addrole.html", AQCGI_REQUEST_METHOD_POST, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqAddRolePost},
{"editrole.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqEditRoleGet},
{"editrole.html", AQCGI_REQUEST_METHOD_POST, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqEditRolePost},
{"delrole.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMMODULES_PERMS_MODULESWRITE, _handleRqDeleteRole},
{NULL, 0, 0, NULL}
};
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModAdmModules_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
}
int AQH_ModAdmModules_Create(AQH_SERVICE *sv)
{
AQH_MODULE *m;
int rv;
m=AQH_Module_new();
AQH_Module_SetName(m, "modules");
AQH_Module_SetDescr(m, "modules administration module");
AQH_Module_SetGuestPerms(m, 0);
_createPermDefList(m);
_createRoleList(m);
rv=AQH_Service_AddModule(sv, m);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Module_free(m);
return rv;
}
void _createPermDefList(AQH_MODULE *m)
{
AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_PermDef_List_new();
AQH_ModService_AddPermDef(permDefList, "ModuleRead", 0x001, "Read modules");
AQH_ModService_AddPermDef(permDefList, "ModuleWrite", 0x002, "Modify modules");
AQH_ModService_AddPermDef(permDefList, "ModuleAdd", 0x004, "Add modules");
AQH_ModService_AddPermDef(permDefList, "ModuleDel", 0x008, "Remove modules");
AQH_Module_SetPermDefList(m, permDefList);
}
void _createRoleList(AQH_MODULE *m)
{
AQH_ROLE_LIST *roleList;
int id=0;
roleList=AQH_Role_List_new();
AQH_ModService_AddRole(roleList, id++, "admin",
AQH_MODADMMODULES_PERMS_MODULESREAD |
AQH_MODADMMODULES_PERMS_MODULESWRITE |
AQH_MODADMMODULES_PERMS_MODULESADD |
AQH_MODADMMODULES_PERMS_MODULESDEL,
"Administrator Role");
AQH_Module_SetRoleList(m, roleList);
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
/* no sub-modules */
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
AQH_ModService_HandleRequestWithTable(m, rq, session, sLastPathElem, _requestTable);
return AQCGI_SendResponse(rq);
}
void _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_STRINGLIST *slModules;
uint32_t perms;
perms=AQH_ModService_GetUserPerms(m);
sv=AQH_ModService_GetService(m);
slModules=AQH_Service_ListModules(sv);
if (slModules) {
GWEN_STRINGLISTENTRY *se;
GBAS(dbuf, "<h1>Modules</h1>\n");
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>"
"<tr><th>Id</th><th>Name</th><th>Description</th><th>Actions</th></tr>\n"
"</thead>\n"
"<tbody>\n");
se=GWEN_StringList_FirstEntry(slModules);
while(se) {
const char *sModName;
sModName=GWEN_StringListEntry_Data(se);
if (sModName && *sModName) {
AQH_MODULE *currentMod;
currentMod=AQH_Service_LoadModule(sv, sModName);
if (currentMod) {
const char *s;
const char *sName;
sName=AQH_Module_GetName(currentMod);
GBAS(dbuf, "<tr>");
GBAA(dbuf, "<td>%lu</td>", (unsigned long int) AQH_Module_GetId(currentMod));
GBAA(dbuf, "<td>%s</td>", sName?sName:"");
s=AQH_Module_GetDescr(currentMod);
GBAA(dbuf, "<td>%s</td>", s?s:"");
GBAS(dbuf, "<td>");
if (perms & AQH_MODADMMODULES_PERMS_MODULESWRITE)
GBAA(dbuf, "<a href=\"editmodule.html?name=%s\"><img src=\"/pics/edit.png\"></a>", sName?sName:"");
GBAA(dbuf, "</td>\n");
GBAA(dbuf, "</tr>\n");
AQH_Module_free(currentMod);
}
}
se=GWEN_StringListEntry_Next(se);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
GWEN_StringList_free(slModules);
}
if (perms & AQH_MODADMMODULES_PERMS_MODULESADD)
GBAS(dbuf, "<hr><a href=\"addmodule.html\">Add Module</a>");
}
void _handleRqEditModGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbQuery;
const char *sModName;
AQH_MODULE *currentMod;
sv=AQH_ModService_GetService(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sModName=dbQuery?GWEN_DB_GetCharValue(dbQuery, "name", 0, NULL):NULL;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
if (currentMod) {
_writeEditModForm(currentMod, sModName, dbuf);
AQH_Module_free(currentMod);
}
else {
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
}
void _handleRqEditModPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sModName;
AQH_MODULE *currentMod;
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sModName=dbPost?GWEN_DB_GetCharValue(dbPost, "module", 0, NULL):NULL;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
if (currentMod) {
const char *sNewModName;
const char *sDescr;
int rv;
uint32_t perms;
const AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_Module_GetPermDefList(currentMod);
sNewModName=GWEN_DB_GetCharValue(dbPost, "name", 0, NULL);
sDescr=GWEN_DB_GetCharValue(dbPost, "descr", 0, NULL);
perms=AQH_ModService_ReadPermsFromForm(dbPost, permDefList, NULL);
if (sNewModName && *sNewModName)
AQH_Module_SetName(currentMod, sNewModName);
AQH_Module_SetDescr(currentMod, sDescr);
AQH_Module_SetGuestPerms(currentMod, perms);
rv=AQH_Service_SaveModule(sv, currentMod);
if (rv<0) {
GBAS(dbuf, "<h2>Error</h2><p>Error saving module</p>");
DBG_ERROR(NULL, "Could not save module \"%s\"", sModName);
AQH_Module_free(currentMod);
return;
}
DBG_ERROR(NULL, "Module \"%s\" saved", sModName);
AQH_Module_free(currentMod);
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
else {
DBG_ERROR(NULL, "Could not load module \"%s\"", sModName?sModName:"<no name>");
GBAS(dbuf, "<p>Error loading module.</p>\n");
}
}
void _handleRqAddRoleGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbQuery;
const char *sModName;
AQH_MODULE *currentMod;
const AQH_PERMDEF_LIST *permDefList;
uint32_t guestPerms;
sv=AQH_ModService_GetService(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sModName=dbQuery?GWEN_DB_GetCharValue(dbQuery, "mod", 0, NULL):NULL;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
guestPerms=currentMod?AQH_Module_GetGuestPerms(currentMod):0;
permDefList=currentMod?AQH_Module_GetPermDefList(currentMod):NULL;
if (currentMod) {
if (permDefList) {
GBAA(dbuf, "<h2>Add Role for Module %s</h2>\n", sModName?sModName:"");
GBAS(dbuf,
"<form action=\"addrole.html\" method=\"post\">\n"
"<table class=\"formtable\">\n"
"<tr><td><label for=\"name\">Name:</label></td><td><input type=\"text\" name=\"name\"></td>"
"<tr><td><label for=\"descr\">Description:</label></td><td><input type=\"text\" name=\"descr\"></td></tr>\n");
GBAS(dbuf, "<tr><td><label>Permissions:</label></td><td>");
AQH_ModService_WritePermsToForm(guestPerms, permDefList, NULL, dbuf);
GBAS(dbuf, "</td></tr>\n");
GBAS(dbuf, "</table>\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"mod\" value=\"%s\">\n", sModName?sModName:"");
GBAS(dbuf, "<input type=\"submit\" value=\"Add\">\n");
GBAS(dbuf, "</form>\n\n");
}
else {
GBAS(dbuf, "<p>Please add permission definitions first.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
}
AQH_Module_free(currentMod);
}
else {
GBAS(dbuf, "<p>Error loading module.</p>\n");
GBAS(dbuf, "<p><a href=\"index.html\"> back to module list</p>\n");
}
}
void _handleRqAddRolePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sModName;
AQH_MODULE *currentMod;
int newId;
const char *sName;
const char *sDescr;
uint32_t perms;
AQH_PERMDEF_LIST *permDefList;
AQH_ROLE_LIST *roleList;
int rv;
/* sample data */
DBG_ERROR(NULL, "Handling POST request");
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sModName=dbPost?GWEN_DB_GetCharValue(dbPost, "mod", 0, NULL):NULL;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
permDefList=currentMod?AQH_Module_GetPermDefList(currentMod):NULL;
roleList=currentMod?AQH_Module_GetRoleList(currentMod):NULL;
/* read role values */
newId=(roleList?_getHighestUsedRoleId(roleList):0)+1;
sName=dbPost?GWEN_DB_GetCharValue(dbPost, "name", 0, NULL):NULL;
sDescr=dbPost?GWEN_DB_GetCharValue(dbPost, "descr", 0, NULL):NULL;
perms=(dbPost && permDefList)?AQH_ModService_ReadPermsFromForm(dbPost, permDefList, NULL):0;
/* validate */
if (!(sName && *sName)) {
DBG_ERROR(NULL, "Missing value for \"name\"");
GBAS(dbuf, "<p>Missing name.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
return;
}
if (currentMod) {
AQH_ROLE *role;
/* set new values */
role=AQH_Role_new();
AQH_Role_SetId(role, newId);
AQH_Role_SetName(role, sName);
AQH_Role_SetDescr(role, sDescr);
AQH_Role_SetPerms(role, perms);
/* add role */
if (roleList==NULL) {
roleList=AQH_Role_List_new();
AQH_Module_SetRoleList(currentMod, roleList);
}
AQH_Role_List_Add(role, roleList);
/* save module */
rv=AQH_Service_SaveModule(sv, currentMod);
if (rv<0) {
GBAS(dbuf, "<p>Error saving module.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
AQH_Module_free(currentMod);
return;
}
_setLocationHeaderForMod(rq, "editmodule.html", sModName);
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See Other");
AQH_Module_free(currentMod);
}
else {
GBAS(dbuf, "<p>Error loading module.</p>\n");
GBAS(dbuf, "<p><a href=\"index.html\"> back to module list</p>\n");
}
}
void _handleRqEditRoleGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbQuery;
const char *sModName;
int id;
const char *sName;
const char *sDescr;
uint32_t perms;
AQH_MODULE *currentMod;
const AQH_PERMDEF_LIST *permDefList;
const AQH_ROLE_LIST *roleList;
const AQH_ROLE *role;
sv=AQH_ModService_GetService(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sModName=dbQuery?GWEN_DB_GetCharValue(dbQuery, "mod", 0, NULL):NULL;
id=dbQuery?GWEN_DB_GetIntValue(dbQuery, "id", 0, 0):0;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
permDefList=currentMod?AQH_Module_GetPermDefList(currentMod):NULL;
roleList=currentMod?AQH_Module_GetRoleList(currentMod):NULL;
role=roleList?AQH_Role_List_GetById(roleList, id):NULL;
sName=role?AQH_Role_GetName(role):NULL;
sDescr=role?AQH_Role_GetDescr(role):NULL;
perms=role?AQH_Role_GetPerms(role):0;
if (role) {
GBAA(dbuf, "<h2>Edit Role for Module %s</h2>\n", sModName?sModName:"");
GBAA(dbuf,
"<form action=\"editrole.html\" method=\"post\">\n"
"<table class=\"formtable\">\n"
"<tr><td><label for=\"name\">Name:</label></td><td><input type=\"text\" name=\"name\" value=\"%s\"></td></tr>\n"
"<tr><td><label for=\"descr\">Description:</label></td><td><input type=\"text\" name=\"descr\" value=\"%s\"></td></tr>\n",
sName, sDescr?sDescr:"");
GBAS(dbuf, "<tr><td><label>Permissions:</label></td><td>");
AQH_ModService_WritePermsToForm(perms, permDefList, NULL, dbuf);
GBAS(dbuf, "</td></tr>\n");
GBAS(dbuf, "</table>\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"mod\" value=\"%s\">\n", sModName?sModName:"");
GBAA(dbuf, "<input type=\"hidden\" name=\"oldId\" value=\"%d\">\n", id);
GBAS(dbuf, "<input type=\"submit\" value=\"Save\">\n");
GBAS(dbuf, "</form>\n\n");
}
else {
GBAS(dbuf, "<p>Role not found.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
}
}
void _handleRqEditRolePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sModName;
AQH_MODULE *currentMod;
int oldId;
const char *sName;
const char *sDescr;
uint32_t perms;
AQH_PERMDEF_LIST *permDefList;
AQH_ROLE_LIST *roleList;
AQH_ROLE *role;
int rv;
/* sample data */
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sModName=dbPost?GWEN_DB_GetCharValue(dbPost, "mod", 0, NULL):NULL;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
permDefList=currentMod?AQH_Module_GetPermDefList(currentMod):NULL;
roleList=currentMod?AQH_Module_GetRoleList(currentMod):NULL;
/* read role data */
oldId=dbPost?GWEN_DB_GetIntValue(dbPost, "oldId", 0, -1):-1;
sName=dbPost?GWEN_DB_GetCharValue(dbPost, "name", 0, NULL):NULL;
sDescr=dbPost?GWEN_DB_GetCharValue(dbPost, "descr", 0, NULL):NULL;
role=roleList?AQH_Role_List_GetById(roleList, oldId):NULL;
perms=(dbPost && permDefList)?AQH_ModService_ReadPermsFromForm(dbPost, permDefList, NULL):0;
/* validate */
if (!(sName && *sName)) {
DBG_ERROR(NULL, "Missing value for \"name\"");
GBAS(dbuf, "<p>Missing name.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
return;
}
if (role==NULL) {
DBG_ERROR(NULL, "Role %d not found", oldId);
GBAS(dbuf, "<p>Role not found.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
return;
}
if (currentMod) {
/* set new values */
AQH_Role_SetName(role, sName);
AQH_Role_SetDescr(role, sDescr);
AQH_Role_SetPerms(role, perms);
/* save module */
rv=AQH_Service_SaveModule(sv, currentMod);
if (rv<0) {
GBAS(dbuf, "<p>Error saving module.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
AQH_Module_free(currentMod);
return;
}
_setLocationHeaderForMod(rq, "editmodule.html", sModName);
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See Other");
AQH_Module_free(currentMod);
}
else {
GBAS(dbuf, "<p>Error loading module.</p>\n");
GBAS(dbuf, "<p><a href=\"index.html\"> back to module list</p>\n");
}
}
void _handleRqDeleteRole(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbQuery;
const char *sModName;
int id;
AQH_MODULE *currentMod;
const AQH_ROLE_LIST *roleList;
AQH_ROLE *role;
sv=AQH_ModService_GetService(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sModName=dbQuery?GWEN_DB_GetCharValue(dbQuery, "mod", 0, NULL):NULL;
id=dbQuery?GWEN_DB_GetIntValue(dbQuery, "id", 0, 0):0;
currentMod=(sModName && *sModName)?AQH_Service_LoadModule(sv, sModName):NULL;
if (currentMod) {
roleList=currentMod?AQH_Module_GetRoleList(currentMod):NULL;
role=roleList?AQH_Role_List_GetById(roleList, id):NULL;
if (role) {
int rv;
AQH_Role_List_Del(role);
AQH_Role_free(role);
/* save module */
rv=AQH_Service_SaveModule(sv, currentMod);
if (rv<0) {
GBAS(dbuf, "<p>Error saving module.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
AQH_Module_free(currentMod);
return;
}
_setLocationHeaderForMod(rq, "editmodule.html", sModName);
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See Other");
}
else {
GBAS(dbuf, "<p>Role not found.</p>\n");
GBAA(dbuf, "<p><a href=\"editmodule.html?name=\"%s\"> back to module</p>\n", sModName?sModName:"");
}
AQH_Module_free(currentMod);
}
else {
GBAS(dbuf, "<p>Error loading module.</p>\n");
GBAS(dbuf, "<p><a href=\"index.html\"> back to module list</p>\n");
}
}
void _setLocationHeaderForMod(AQCGI_REQUEST *rq, const char *page, const char *sModName)
{
GWEN_BUFFER *tbuf;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAA(tbuf, "Location: %s?name=%s", page?page:"", sModName?sModName:"");
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
GWEN_Buffer_free(tbuf);
}
int _getHighestUsedRoleId(const AQH_ROLE_LIST *roleList)
{
int id=0;
if (roleList) {
const AQH_ROLE *role;
role=AQH_Role_List_First(roleList);
while(role) {
int rid;
rid=AQH_Role_GetId(role);
id=(rid>id)?rid:id;
role=AQH_Role_List_Next(role);
}
}
return id;
}
void _writeEditModForm(const AQH_MODULE *currentMod, const char *sModName, GWEN_BUFFER *dbuf)
{
const char *sName;
const char *sDescr;
const AQH_PERMDEF_LIST *permDefList;
const AQH_ROLE_LIST *roleList;
permDefList=AQH_Module_GetPermDefList(currentMod);
roleList=AQH_Module_GetRoleList(currentMod);
sName=AQH_Module_GetName(currentMod);
sDescr=AQH_Module_GetDescr(currentMod);
/* write module info */
GBAS(dbuf, "<h2>Module Info</h2>\n");
GBAA(dbuf,
"<form action=\"editmodule.html\" method=\"post\">\n"
"<table class=\"formtable\">\n"
"<tr><td><label for=\"name\">Name:</label></td><td><input type=\"text\" name=\"name\" value=\"%s\"></td></tr>\n"
"<tr><td><label for=\"descr\">Description:</label></td><td><input type=\"text\" name=\"descr\" value=\"%s\"></td></tr>\n",
sName?sName:"", sDescr?sDescr:"");
if (permDefList) {
GBAA(dbuf, "<tr><td><label>Guest Permissions:</label></td>\n<td>");
AQH_ModService_WritePermsToForm(AQH_Module_GetGuestPerms(currentMod), permDefList, NULL, dbuf);
GBAA(dbuf, "</td></tr>");
}
GBAS(dbuf, "</table>\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"module\" value=\"%s\">\n", sModName?sModName:"");
GBAS(dbuf, "<input type=\"submit\" value=\"Save\">\n</form>\n\n");
/* write role list */
GBAS(dbuf, "<h2>User Roles</h2>\n");
if (roleList)
_writeRoleListToForm(roleList, sModName, permDefList, dbuf);
else
GBAS(dbuf, "<p>none</p>");
GBAA(dbuf, "<a href=\"addrole.html?mod=%s\"><img src=\"/pics/plus.png\">Add Role</a>\n", sModName?sModName:"");
}
void _writeRoleListToForm(const AQH_ROLE_LIST *roleList,
const char *sModName,
const AQH_PERMDEF_LIST *permDefList,
GWEN_BUFFER *dbuf)
{
const AQH_ROLE *role;
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead><tr><th>Id</th><th>Name</th><th>Permissions</th><th>Description</th><th>Actions</th></tr>\n</thead>\n"
"<tbody>\n");
role=AQH_Role_List_First(roleList);
while(role) {
uint8_t id;
const char *s;
GBAS(dbuf, "<tr>");
/* id */
id=AQH_Role_GetId(role);
GBAA(dbuf, "<td>%d</td>", id);
/* name */
s=AQH_Role_GetName(role);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* permissions */
GBAS(dbuf, "<td>");
if (permDefList)
_writeEnabledPermissions(permDefList, AQH_Role_GetPerms(role), dbuf);
GBAS(dbuf, "</td>");
/* description */
s=AQH_Role_GetDescr(role);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* actions */
GBAA(dbuf, "<td>");
GBAA(dbuf, "<a href=\"editrole.html?mod=%s&id=%d\"><img src=\"/pics/edit.png\"></a>", sModName?sModName:"", id);
GBAA(dbuf, "<a href=\"delrole.html?mod=%s&id=%d\"><img src=\"/pics/minus.png\"></a>", sModName?sModName:"", id);
GBAA(dbuf, "</td>");
GBAS(dbuf, "</tr>\n");
role=AQH_Role_List_Next(role);
}
GBAS(dbuf, "</tbody>\n</table>\n");
}
void _writeEnabledPermissions(const AQH_PERMDEF_LIST *permDefList, uint32_t perms, GWEN_BUFFER *dbuf)
{
if (permDefList) {
const AQH_PERMDEF *permDef;
permDef=AQH_PermDef_List_First(permDefList);
while(permDef) {
const char *s;
uint32_t mask;
s=AQH_PermDef_GetId(permDef);
mask=AQH_PermDef_GetMask(permDef);
if (perms & mask)
GBAA(dbuf, "%s ", s?s:"");
permDef=AQH_PermDef_List_Next(permDef);
}
}
}

View File

@@ -0,0 +1,35 @@
/****************************************************************************
* 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_CGI_MMODULES_H
#define AQHOME_CGI_MMODULES_H
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
#define AQH_MODADMMODULES_PERMS_MODULESREAD 0x001
#define AQH_MODADMMODULES_PERMS_MODULESWRITE 0x002
#define AQH_MODADMMODULES_PERMS_MODULESADD 0x004
#define AQH_MODADMMODULES_PERMS_MODULESDEL 0x008
void AQH_ModAdmModules_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
int AQH_ModAdmModules_Create(AQH_SERVICE *sv);
#endif

View File

@@ -0,0 +1,820 @@
/****************************************************************************
* 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 "./mservice_p.h"
#include "aqhome-cgi/service/module.h"
#include <gwenhywfar/debug.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define AQH_MOD_SERVICE_HEADERFILE "header.html"
#define AQH_MOD_SERVICE_FOOTERFILE "footer.html"
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
GWEN_INHERIT(AQH_MODULE, AQH_MOD_SERVICE)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static void _calcUserModPerms(AQH_MODULE *m, const AQH_USER *user);
static uint32_t _calcRolePerms(const AQH_MODULE *m, const AQH_MODULE_PERMS *modPerms);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModService_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MOD_SERVICE *xm;
GWEN_NEW_OBJECT(AQH_MOD_SERVICE, xm);
GWEN_INHERIT_SETDATA(AQH_MODULE, AQH_MOD_SERVICE, m, xm, _freeData);
xm->service=sv;
xm->baseFolder=(baseFolder && *baseFolder)?strdup(baseFolder):NULL;
}
void _freeData(GWEN_UNUSED void *bp, void *p)
{
AQH_MOD_SERVICE *xm;
xm=(AQH_MOD_SERVICE*) p;
free(xm->baseFolder);
GWEN_FREE_OBJECT(xm);
}
AQH_SERVICE *AQH_ModService_GetService(const AQH_MODULE *m)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
return xm->service;
}
}
return NULL;
}
const char *AQH_ModService_GetBaseFolder(const AQH_MODULE *m)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
return xm->baseFolder;
}
}
return NULL;
}
uint32_t AQH_ModService_GetUserPerms(const AQH_MODULE *m)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
return xm->userPerms;
}
}
return 0;
}
void AQH_ModService_SetHandleRequestFn(AQH_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->handleRequestFn=fn;
}
}
}
void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->loadSubModuleFn=fn;
}
}
}
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->addHeaderFn=fn;
}
}
}
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->addFooterFn=fn;
}
}
}
void AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
if (xm->addHeaderFn)
xm->addHeaderFn(m, lang, dbuf);
else {
AQH_MODULE *mParent;
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent)
AQH_ModService_AddHeader(mParent, lang, dbuf);
AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_HEADERFILE, dbuf);
}
}
}
}
void AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
if (xm->addFooterFn)
xm->addFooterFn(m, lang, dbuf);
else {
AQH_MODULE *mParent;
AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_FOOTERFILE, dbuf);
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent)
AQH_ModService_AddFooter(mParent, lang, dbuf);
}
}
}
}
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename, GWEN_BUFFER *dbuf)
{
int rv;
rv=AQH_ModService_ReadStaticFile(m, lang, sFilename, dbuf);
if (rv<0) {
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
return GWEN_ERROR_INTERNAL;
}
AQCGI_Request_SetResponseCode(rq, 200);
AQCGI_Request_SetResponseText(rq, "Ok");
return 0;
}
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm && xm->handleRequestFn)
return xm->handleRequestFn(m, rq, session, sLastPathElem);
}
return GWEN_ERROR_NOT_IMPLEMENTED;
}
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm && xm->loadSubModuleFn) {
AQH_MODULE *mReturn;
mReturn=xm->loadSubModuleFn(m, rq, session, sModuleName);
if (mReturn)
AQH_ModService_CalcSessionModPerms(mReturn, session);
return mReturn;
}
}
return NULL;
}
int AQH_ModService_ReadStaticFile(AQH_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf)
{
if (m && filename && dbuf) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
GWEN_BUFFER *fbuf;
int rv;
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendString(fbuf, xm->baseFolder);
GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S);
GWEN_Buffer_AppendString(fbuf, (lang && *lang)?lang:"en");
GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S);
GWEN_Buffer_AppendString(fbuf, filename);
DBG_ERROR(NULL, "Reading file \"%s\"", GWEN_Buffer_GetStart(fbuf));
rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fbuf), dbuf);
if (rv<0) {
DBG_ERROR(NULL, "Read(%s): %d", GWEN_Buffer_GetStart(fbuf), rv);
GWEN_Buffer_free(fbuf);
return rv;
}
GWEN_Buffer_free(fbuf);
return 0;
}
}
DBG_ERROR(NULL, "Any arg is missing (or is not a AQH_MOD_SERVICE object)");
return GWEN_ERROR_INTERNAL;
}
AQH_SESSION *AQH_ModService_ReadSession(AQH_MODULE *m, AQCGI_REQUEST *rq)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *db;
const char *s;
sv=AQH_ModService_GetService(m);
db=AQCGI_Request_GetDbRequestHeader(rq);
s=GWEN_DB_GetCharValue(db, "cookies/session", 0, NULL);
if (s && *s) {
AQH_SESSION *session;
session=AQH_Service_LoadSession(sv, s);
if (session==NULL) {
DBG_ERROR(NULL, "Session \"%s\" not found", s);
return NULL;
}
else {
const char *sUserName;
GWEN_BUFFER *tbuf;
sUserName=AQH_Session_GetUserAlias(session);
if (sUserName && *sUserName) {
AQH_USER *user;
user=AQH_Service_LoadUser(sv, sUserName);
if (user==NULL) {
DBG_ERROR(NULL, "User \"%s\" not found", sUserName);
AQH_Session_free(session);
return NULL;
}
else {
DBG_ERROR(NULL, "User is \"%s\"", sUserName);
}
AQH_Session_SetUser(session, user);
}
/* renew session cookie */
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(tbuf, "Set-Cookie: session=%s; max-age=86400", AQH_Session_GetUid(session));
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
DBG_ERROR(NULL, "Renew session cookie");
GWEN_Buffer_free(tbuf);
return session;
}
}
else {
DBG_ERROR(NULL, "No session cookie");
}
return NULL;
}
void AQH_ModService_CalcSessionModPerms(AQH_MODULE *m, const AQH_SESSION *session)
{
const AQH_USER *user;
user=session?AQH_Session_GetUser(session):NULL;
_calcUserModPerms(m, user);
}
void _calcUserModPerms(AQH_MODULE *m, const AQH_USER *user)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
uint32_t perms=0;
if (user) {
if (AQH_User_GetFlags(user) & AQH_USER_FLAGS_ADMIN)
perms=0xffffffff;
else {
const char *sModName;
const AQH_MODULE_PERMS_LIST *modPermsList;
AQH_MODULE_PERMS *modPerms;
sModName=AQH_Module_GetName(m);
modPermsList=AQH_User_GetModulePermList(user);
modPerms=(sModName && modPermsList)?AQH_ModulePerms_List_GetByModuleId(modPermsList, sModName):NULL;
if (modPerms)
perms=_calcRolePerms(m, modPerms);
else
perms=AQH_Module_GetGuestPerms(m);
}
} /* if (user) */
else
perms=AQH_Module_GetGuestPerms(m);
xm->userPerms=perms;
}
} /* if (m) */
}
uint32_t _calcRolePerms(const AQH_MODULE *m, const AQH_MODULE_PERMS *modPerms)
{
uint32_t perms=0;
const AQH_ROLE_LIST *roleList;
roleList=AQH_Module_GetRoleList(m);
if (roleList) {
int roleArraySize;
int i;
uint32_t explAddPerms=0;
uint32_t explDelPerms=0;
roleArraySize=AQH_ModulePerms_GetRoleArrayArraySize();
for (i=0; i<roleArraySize; i++) {
int roleId;
roleId=AQH_ModulePerms_GetRoleArrayAt(modPerms, i);
if (roleId) {
const AQH_ROLE *role;
role=AQH_Role_List_GetById(roleList, roleId);
if (role) {
perms|=AQH_Role_GetPerms(role);
explAddPerms|=AQH_Role_GetExplAddPerms(role);
explAddPerms|=AQH_Role_GetExplDelPerms(role);
}
}
} /* for */
/* collate permissions */
perms|=explAddPerms;
perms|=AQH_ModulePerms_GetExplAddPerms(modPerms);
perms&=~explDelPerms;
perms&=~AQH_ModulePerms_GetExplDelPerms(modPerms);
}
return perms;
}
void AQH_ModService_AddPermDef(AQH_PERMDEF_LIST *permDefList, const char *id, uint32_t mask, const char *descr)
{
AQH_PERMDEF *permDef;
permDef=AQH_PermDef_new();
AQH_PermDef_SetId(permDef, id);
AQH_PermDef_SetMask(permDef, mask);
AQH_PermDef_SetDescr(permDef, descr);
AQH_PermDef_List_Add(permDef, permDefList);
}
void AQH_ModService_AddRole(AQH_ROLE_LIST *roleList, int id, const char *name, uint32_t perms, const char *descr)
{
AQH_ROLE *role;
role=AQH_Role_new();
AQH_Role_SetId(role, id);
AQH_Role_SetName(role, name);
AQH_Role_SetPerms(role, perms);
AQH_Role_SetDescr(role, descr);
AQH_Role_List_Add(role, roleList);
}
void AQH_ModService_HandleRequestWithTable(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
const char *page,
const AQH_MODSERVICE_HANDLER_ENTRY *e)
{
uint32_t perms;
GWEN_BUFFER *dbuf;
int i;
perms=AQH_ModService_GetUserPerms(m);
DBG_ERROR(NULL, "Perms=%08x", perms);
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_ModService_AddHeader(m, "en", dbuf);
for (i=0; ; i++) {
if (e[i].page==NULL) {
AQCGI_Request_SetResponseCode(rq, 404);
AQCGI_Request_SetResponseText(rq, "Not Found");
break;
}
if ((AQCGI_Request_GetRequestMethod(rq)==e[i].httpMethod) &&
(strcasecmp(page, e[i].page)==0)) {
/* preset result */
AQCGI_Request_SetResponseCode(rq, 200);
AQCGI_Request_SetResponseText(rq, "Ok");
if ((perms & e[i].perms)==e[i].perms)
(e[i].handlerFn)(m, rq, session, dbuf);
else {
GWEN_Buffer_AppendString(dbuf, "<h1>Error</h1><p>No permissions for this request.</p>");
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
}
break;
}
}
if (!(AQCGI_Request_GetFlags(rq) & AQH_MODSERVICE_RQFLAGS_RAWFILE)) {
DBG_ERROR(NULL, "Not adding footer");
AQH_ModService_AddFooter(m, "en", dbuf);
}
AQCGI_Request_SetBufferResponseBody(rq, dbuf);
if (AQCGI_Request_GetFlags(rq) & AQCGI_FLAGS_HAS_CONTENT_HEADER)
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
}
void AQH_ModService_WritePermsToForm(uint32_t perms, const AQH_PERMDEF_LIST *permDefList, const char *sPrefix, GWEN_BUFFER *dbuf)
{
if (permDefList) {
const AQH_PERMDEF *permDef;
GWEN_BUFFER *tbuf;
uint32_t pos;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
if (sPrefix && *sPrefix)
GBAA(tbuf, "%s:", sPrefix);
pos=GWEN_Buffer_GetPos(tbuf);
permDef=AQH_PermDef_List_First(permDefList);
while(permDef) {
const char *s;
s=AQH_PermDef_GetId(permDef);
if (s && *s) {
uint32_t mask;
GBAS(tbuf, s);
mask=AQH_PermDef_GetMask(permDef);
if (perms & mask)
GBAA(dbuf, "<input type=\"checkbox\" name=\"%s\" checked>", GWEN_Buffer_GetStart(tbuf));
else
GBAA(dbuf, "<input type=\"checkbox\" name=\"%s\">", s?s:"");
GBAA(dbuf, "<label for=\"%s\">%s</label>", GWEN_Buffer_GetStart(tbuf), s?s:"");
GWEN_Buffer_Crop(tbuf, 0, pos);
}
permDef=AQH_PermDef_List_Next(permDef);
}
GWEN_Buffer_free(tbuf);
}
}
uint32_t AQH_ModService_ReadPermsFromForm(GWEN_DB_NODE *dbPost, const AQH_PERMDEF_LIST *permDefList, const char *sPrefix)
{
uint32_t result=0;
if (permDefList) {
const AQH_PERMDEF *permDef;
GWEN_BUFFER *tbuf;
uint32_t pos;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
if (sPrefix && *sPrefix)
GBAA(tbuf, "%s:", sPrefix);
pos=GWEN_Buffer_GetPos(tbuf);
permDef=AQH_PermDef_List_First(permDefList);
while(permDef) {
const char *id;
id=AQH_PermDef_GetId(permDef);
if (id && *id) {
uint32_t mask;
const char *s;
GBAS(tbuf, id);
mask=AQH_PermDef_GetMask(permDef);
s=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(tbuf), 0, NULL);
if (s && *s)
result|=mask;
GWEN_Buffer_Crop(tbuf, 0, pos);
}
permDef=AQH_PermDef_List_Next(permDef);
}
GWEN_Buffer_free(tbuf);
}
return result;
}
AQH_MODULE_LIST *AQH_ModService_LoadRawModules(AQH_MODULE *m)
{
AQH_SERVICE *sv;
GWEN_STRINGLIST *slModuleNames;
sv=AQH_ModService_GetService(m);
slModuleNames=AQH_Service_ListModules(sv);
if (slModuleNames) {
AQH_MODULE_LIST *modList;
GWEN_STRINGLISTENTRY *se;
modList=AQH_Module_List_new();
se=GWEN_StringList_FirstEntry(slModuleNames);
while(se) {
const char *sModName;
sModName=GWEN_StringListEntry_Data(se);
if (sModName && *sModName) {
AQH_MODULE *currentMod;
currentMod=AQH_Service_LoadModule(sv, sModName);
if (currentMod)
AQH_Module_List_Add(currentMod, modList);
}
se=GWEN_StringListEntry_Next(se);
}
GWEN_StringList_free(slModuleNames);
if (AQH_Module_List_GetCount(modList))
return modList;
AQH_Module_List_free(modList);
}
return NULL;
}
AQH_USER_LIST *AQH_ModService_LoadRawUsers(AQH_MODULE *m)
{
AQH_SERVICE *sv;
GWEN_STRINGLIST *slUserNames;
sv=AQH_ModService_GetService(m);
slUserNames=AQH_Service_ListUsers(sv);
if (slUserNames) {
AQH_USER_LIST *userList;
GWEN_STRINGLISTENTRY *se;
userList=AQH_User_List_new();
se=GWEN_StringList_FirstEntry(slUserNames);
while(se) {
const char *sModName;
sModName=GWEN_StringListEntry_Data(se);
if (sModName && *sModName) {
AQH_USER *u;
u=AQH_Service_LoadUser(sv, sModName);
if (u)
AQH_User_List_Add(u, userList);
}
se=GWEN_StringListEntry_Next(se);
}
GWEN_StringList_free(slUserNames);
if (AQH_User_List_GetCount(userList))
return userList;
AQH_User_List_free(userList);
}
return NULL;
}
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf)
{
while (*src) {
unsigned char x;
x=(unsigned char)*src;
if (!(
(x>='A' && x<='Z') ||
(x>='a' && x<='z') ||
(x>='0' && x<='9') ||
NULL!=strchr("-_", x)
)) {
unsigned char c;
GWEN_Buffer_AppendByte(buf, '%');
c=(((unsigned char)(*src))>>4)&0xf;
if (c>9)
c+=7;
c+='0';
GWEN_Buffer_AppendByte(buf, c);
c=((unsigned char)(*src))&0xf;
if (c>9)
c+=7;
c+='0';
GWEN_Buffer_AppendByte(buf, c);
}
else
GWEN_Buffer_AppendByte(buf, *src);
src++;
} /* while */
}
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf)
{
while (*src) {
int charHandled=0;
if (*src=='%') {
if (strlen(src)>2) {
unsigned char d1, d2;
unsigned char c;
if (isxdigit((int)src[1]) && isxdigit((int)src[2])) {
/* skip '%' */
src++;
/* read first digit */
d1=(unsigned char)(toupper(*src));
/* get second digit */
src++;
d2=(unsigned char)(toupper(*src));
/* compute character */
d1-='0';
if (d1>9)
d1-=7;
c=(d1<<4)&0xf0;
d2-='0';
if (d2>9)
d2-=7;
c+=(d2&0xf);
/* store character */
GWEN_Buffer_AppendByte(buf, (char)c);
charHandled=1;
}
}
}
if (!charHandled)
GWEN_Buffer_AppendByte(buf, *src);
src++;
} /* while */
}
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds)
{
struct stat sb;
time_t t1;
if (lstat(sPath, &sb)==-1) {
DBG_ERROR(NULL, "Error on lstat(%s): %s (%d)", sPath, strerror(errno), errno);
return 0;
}
t1=time(0);
if ((t1-sb.st_mtime)<(time_t) seconds) {
DBG_DEBUG(NULL, "File %s is current", sPath);
return 1;
}
return 0;
}
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf)
{
GWEN_BUFFER *ibuf;
int rv;
ibuf=GWEN_Buffer_new(0, 1024, 0, 1);
/* read file */
rv=GWEN_SyncIo_Helper_ReadFile(sFilename, ibuf);
if (rv<0) {
DBG_ERROR(NULL, "Error reading \"%s\" (%d)", sFilename, rv);
return rv;
}
else {
GWEN_Buffer_Reset(dbuf);
GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf));
if (sMimeType && *sMimeType) {
GWEN_BUFFER *tbuf;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAA(tbuf, "Content-type: %s", sMimeType);
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
GWEN_Buffer_free(tbuf);
}
AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE);
}
GWEN_Buffer_free(ibuf);
return 0;
}

View File

@@ -0,0 +1,91 @@
/****************************************************************************
* 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_CGI_MSERVICE_H
#define AQHOME_CGI_MSERVICE_H
#include <aqhome-cgi/service/module.h>
#include <aqhome-cgi/service/service.h>
#include <aqhome-cgi/service/session.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
#define AQH_MODSERVICE_RQFLAGS_RAWFILE 0x10000000
typedef int (*AQH_MODSERVICE_HANDLEREQUEST_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
typedef AQH_MODULE* (*AQH_MODSERVICE_LOADSUBMODULE_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
typedef void (*AQH_MODSERVICE_ADDHEADER_FN)(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
typedef void (*AQH_MODSERVICE_ADDFOOTER_FN)(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
typedef struct AQH_MODSERVICE_HANDLER_ENTRY AQH_MODSERVICE_HANDLER_ENTRY;
struct AQH_MODSERVICE_HANDLER_ENTRY {
const char *page;
int httpMethod;
uint32_t perms;
void (*handlerFn)(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
};
void AQH_ModService_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
AQH_SERVICE *AQH_ModService_GetService(const AQH_MODULE *m);
const char *AQH_ModService_GetBaseFolder(const AQH_MODULE *m);
uint32_t AQH_ModService_GetUserPerms(const AQH_MODULE *m);
void AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
void AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
AQH_SESSION *AQH_ModService_ReadSession(AQH_MODULE *m, AQCGI_REQUEST *rq);
void AQH_ModService_CalcSessionModPerms(AQH_MODULE *m, const AQH_SESSION *session);
void AQH_ModService_HandleRequestWithTable(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
const char *page,
const AQH_MODSERVICE_HANDLER_ENTRY *e);
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename, GWEN_BUFFER *dbuf);
int AQH_ModService_ReadStaticFile(AQH_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf);
void AQH_ModService_AddPermDef(AQH_PERMDEF_LIST *permDefList, const char *id, uint32_t mask, const char *descr);
void AQH_ModService_AddRole(AQH_ROLE_LIST *roleList, int id, const char *name, uint32_t perms, const char *descr);
void AQH_ModService_WritePermsToForm(uint32_t perms, const AQH_PERMDEF_LIST *permDefList, const char *sPrefix, GWEN_BUFFER *dbuf);
uint32_t AQH_ModService_ReadPermsFromForm(GWEN_DB_NODE *dbPost, const AQH_PERMDEF_LIST *permDefList, const char *sPrefix);
AQH_MODULE_LIST *AQH_ModService_LoadRawModules(AQH_MODULE *m);
AQH_USER_LIST *AQH_ModService_LoadRawUsers(AQH_MODULE *m);
void AQH_ModService_SetHandleRequestFn(AQH_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn);
void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn);
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn);
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn);
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf);
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf);
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds);
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,30 @@
/****************************************************************************
* 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_CGI_MSERVICE_P_H
#define AQHOME_CGI_MSERVICE_P_H
#include "aqhome-cgi/modules/common/mservice.h"
typedef struct AQH_MOD_SERVICE AQH_MOD_SERVICE;
struct AQH_MOD_SERVICE {
AQH_SERVICE *service;
char *baseFolder;
uint32_t userPerms;
AQH_MODSERVICE_HANDLEREQUEST_FN handleRequestFn;
AQH_MODSERVICE_LOADSUBMODULE_FN loadSubModuleFn;
AQH_MODSERVICE_ADDHEADER_FN addHeaderFn;
AQH_MODSERVICE_ADDFOOTER_FN addFooterFn;
};
#endif

View File

@@ -0,0 +1,640 @@
/****************************************************************************
* 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 "./musers.h"
#include "aqhome-cgi/service/module.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _createPermDefList(AQH_MODULE *m);
static void _createRoleList(AQH_MODULE *m);
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static void _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditUserGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqEditUserPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqAddUserGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqAddUserPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static int _getHighestUserId(const AQH_USER_LIST *userList);
static int _modulePermsHasRole(const AQH_MODULE_PERMS *modPerms, uint8_t rid);
static void _writeEditUserForm(AQH_MODULE *m,
const AQH_USER *u,
const char *sAlias,
const char *sUrl,
const char *sSubmitText,
GWEN_BUFFER *dbuf);
static void _writeUserModRolesToForm(const AQH_ROLE_LIST *roles, const AQH_MODULE_PERMS *perms, const char *sModName, GWEN_BUFFER *dbuf);
static void _readAllModRolesForUserFromForm(AQH_MODULE *m, GWEN_DB_NODE *dbPost, AQH_USER *u);
static void _readModRolesFromForm(GWEN_DB_NODE *dbPost,
const AQH_ROLE_LIST *roleList,
const char *sPrefix,
AQH_MODULE_PERMS *modPerms);
static void _addLabelAndInputToFormTableH(const char *title, const char *name, const char *value, const char *xxtra, GWEN_BUFFER *dbuf);
static void _addUserStateLabelAndSelectionToFormTableH(const char *sTitle, const char *sName, int st, GWEN_BUFFER *dbuf);
static void _setLocationHeaderForMod(AQCGI_REQUEST *rq, const char *page, const char *sModName);
/* ------------------------------------------------------------------------------------------------
* vars
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
{"index.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMUSERS_PERMS_USERSREAD, _handleRqIndex},
{"edituser.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMUSERS_PERMS_USERSWRITE, _handleRqEditUserGet},
{"edituser.html", AQCGI_REQUEST_METHOD_POST, AQH_MODADMUSERS_PERMS_USERSWRITE, _handleRqEditUserPost},
{"adduser.html", AQCGI_REQUEST_METHOD_GET, AQH_MODADMUSERS_PERMS_USERSWRITE, _handleRqAddUserGet},
{"adduser.html", AQCGI_REQUEST_METHOD_POST, AQH_MODADMUSERS_PERMS_USERSWRITE, _handleRqAddUserPost},
{NULL, 0, 0, NULL}
};
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModAdmUsers_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
}
int AQH_ModAdmUsers_Create(AQH_SERVICE *sv)
{
AQH_MODULE *m;
int rv;
m=AQH_Module_new();
AQH_Module_SetName(m, "users");
AQH_Module_SetDescr(m, "user administration module");
AQH_Module_SetGuestPerms(m, 0);
_createPermDefList(m);
_createRoleList(m);
rv=AQH_Service_AddModule(sv, m);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Module_free(m);
return rv;
}
void _createPermDefList(AQH_MODULE *m)
{
AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_PermDef_List_new();
AQH_ModService_AddPermDef(permDefList, "UserRead", 0x001, "Read users");
AQH_ModService_AddPermDef(permDefList, "UserWrite", 0x002, "Modify users");
AQH_ModService_AddPermDef(permDefList, "UserAdd", 0x004, "Add users");
AQH_ModService_AddPermDef(permDefList, "UserDel", 0x008, "Remove users");
AQH_Module_SetPermDefList(m, permDefList);
}
void _createRoleList(AQH_MODULE *m)
{
AQH_ROLE_LIST *roleList;
int id=0;
roleList=AQH_Role_List_new();
AQH_ModService_AddRole(roleList, id++, "admin",
AQH_MODADMUSERS_PERMS_USERSREAD |
AQH_MODADMUSERS_PERMS_USERSWRITE |
AQH_MODADMUSERS_PERMS_USERSADD |
AQH_MODADMUSERS_PERMS_USERSDEL,
"Administrator Role");
AQH_Module_SetRoleList(m, roleList);
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
/* no sub-modules */
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
AQH_ModService_HandleRequestWithTable(m, rq, session, sLastPathElem, _requestTable);
return AQCGI_SendResponse(rq);
}
void _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_USER_LIST *userList;
uint32_t perms;
perms=AQH_ModService_GetUserPerms(m);
userList=AQH_ModService_LoadRawUsers(m);
GBAS(dbuf, "<h1>Users</h1>\n");
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>"
"<tr><th>Id</th><th>Alias</th><th>Name</th><th>Status</th><th>Email</th><th>Notes</th><th>Actions</th></tr>\n"
"</thead>\n"
"<tbody>\n");
if (userList) {
const AQH_USER *u;
AQH_User_List_SortByAlias(userList, 1);
u=AQH_User_List_First(userList);
while(u) {
const char *sUserAlias;
sUserAlias=AQH_User_GetAlias(u);
if (sUserAlias && *sUserAlias) {
uint32_t id;
const char *s;
const char *sAlias;
id=AQH_User_GetId(u);
sAlias=AQH_User_GetAlias(u);
GBAS(dbuf, "<tr>");
GBAA(dbuf, "<td>%lu</td>", (unsigned long int) id);
GBAA(dbuf, "<td>%s</td>", sAlias?sAlias:"");
s=AQH_User_GetName(u);
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_UserState_toString(AQH_User_GetState(u));
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_User_GetEmail(u);
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_User_GetNotes(u);
GBAA(dbuf, "<td>%s</td>", s?s:"");
GBAS(dbuf, "<td>");
if (perms & AQH_MODADMUSERS_PERMS_USERSWRITE) {
DBG_ERROR(NULL, "User=%s", sAlias?sAlias:"");
GBAS(dbuf, "<a href=\"edituser.html?alias=");
GWEN_Text_EscapeToBufferTolerant(sAlias?sAlias:"", dbuf);
GBAS(dbuf, "\"><img src=\"/pics/edit.png\"></a>");
}
GBAA(dbuf, "</td>\n");
GBAA(dbuf, "</tr>\n");
}
u=AQH_User_List_Next(u);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
AQH_User_List_free(userList);
}
if (perms & AQH_MODADMUSERS_PERMS_USERSADD)
GBAS(dbuf, "<hr><a href=\"adduser.html\">Add User</a>");
}
void _handleRqEditUserGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbQuery;
const char *sAlias;
AQH_USER *user;
sv=AQH_ModService_GetService(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sAlias=dbQuery?GWEN_DB_GetCharValue(dbQuery, "alias", 0, NULL):NULL;
user=(sAlias && *sAlias)?AQH_Service_LoadUser(sv, sAlias):NULL;
if (user) {
_writeEditUserForm(m, user, sAlias, "edituser.html", "Save", dbuf);
AQH_User_free(user);
}
else {
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
}
void _handleRqEditUserPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sAlias;
AQH_USER *u;
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sAlias=dbPost?GWEN_DB_GetCharValue(dbPost, "alias", 0, NULL):NULL;
u=(sAlias && *sAlias)?AQH_Service_LoadUser(sv, sAlias):NULL;
if (u) {
const char *s;
int state;
int rv;
s=GWEN_DB_GetCharValue(dbPost, "name", 0, NULL);
AQH_User_SetName(u, s);
s=GWEN_DB_GetCharValue(dbPost, "email", 0, NULL);
AQH_User_SetEmail(u, s);
s=GWEN_DB_GetCharValue(dbPost, "notes", 0, NULL);
AQH_User_SetNotes(u, s);
s=GWEN_DB_GetCharValue(dbPost, "status", 0, NULL);
state=(s && *s)?AQH_UserState_fromString(s):AQH_UserState_Unknown;
if (state!=AQH_UserState_Unknown)
AQH_User_SetState(u, state);
_readAllModRolesForUserFromForm(m, dbPost, u);
rv=AQH_Service_SaveUser(sv, u);
if (rv<0) {
GBAS(dbuf, "<h2>Error</h2><p>Error saving user</p>");
DBG_ERROR(NULL, "Could not save user \"%s\"", sAlias);
AQH_User_free(u);
return;
}
DBG_ERROR(NULL, "User \"%s\" saved", sAlias);
AQH_User_free(u);
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
else {
DBG_ERROR(NULL, "Could not load user \"%s\"", sAlias?sAlias:"<no name>");
GBAS(dbuf, "<p>Error loading user.</p>\n");
}
}
void _handleRqAddUserGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
DBG_ERROR(NULL, "AddUser");
_writeEditUserForm(m, NULL, NULL, "adduser.html", "Add", dbuf);
}
void _handleRqAddUserPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sAlias;
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sAlias=dbPost?GWEN_DB_GetCharValue(dbPost, "alias", 0, NULL):NULL;
// TODO: check alias validity
if (sAlias) {
AQH_USER *u;
const char *s;
int state;
int rv;
uint32_t userId;
AQH_USER_LIST *userList;
u=AQH_User_new();
userList=AQH_ModService_LoadRawUsers(m);
userId=_getHighestUserId(userList)+1;
AQH_User_SetId(u, userId);
AQH_User_SetAlias(u, sAlias);
s=GWEN_DB_GetCharValue(dbPost, "name", 0, NULL);
AQH_User_SetName(u, s);
s=GWEN_DB_GetCharValue(dbPost, "email", 0, NULL);
AQH_User_SetEmail(u, s);
s=GWEN_DB_GetCharValue(dbPost, "notes", 0, NULL);
AQH_User_SetNotes(u, s);
s=GWEN_DB_GetCharValue(dbPost, "status", 0, NULL);
state=(s && *s)?AQH_UserState_fromString(s):AQH_UserState_Unknown;
if (state!=AQH_UserState_Unknown)
AQH_User_SetState(u, state);
rv=AQH_Service_AddUser(sv, u);
if (rv<0) {
GBAS(dbuf, "<h2>Error</h2><p>Error saving user</p>");
DBG_ERROR(NULL, "Could not save user \"%s\" (%d)", sAlias, rv);
AQH_User_free(u);
AQH_User_List_free(userList);
return;
}
DBG_ERROR(NULL, "User \"%s\" saved", sAlias);
AQH_User_free(u);
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
AQH_User_List_free(userList);
}
else {
DBG_ERROR(NULL, "Missing alias");
GBAS(dbuf, "<p>Missing alias.</p>\n");
}
}
int _getHighestUserId(const AQH_USER_LIST *userList)
{
int id=0;
if (userList) {
const AQH_USER *user;
user=AQH_User_List_First(userList);
while(user) {
int uid;
uid=AQH_User_GetId(user);
id=(uid>id)?uid:id;
user=AQH_User_List_Next(user);
}
}
return id;
}
int _modulePermsHasRole(const AQH_MODULE_PERMS *modPerms, uint8_t rid)
{
if (modPerms) {
int arraySize;
int i;
arraySize=AQH_ModulePerms_GetRoleArrayArraySize();
for(i=0; i<arraySize; i++) {
if (AQH_ModulePerms_GetRoleArrayAt(modPerms, i)==rid)
return 1;
}
}
return 0;
}
void _writeEditUserForm(AQH_MODULE *m,
const AQH_USER *u,
const char *sAlias,
const char *sUrl,
const char *sSubmitText,
GWEN_BUFFER *dbuf)
{
AQH_MODULE_LIST *moduleList;
/* write user info */
GBAS(dbuf, "<h2>User Info</h2>\n");
GBAA(dbuf,
"<form action=\"%s\" method=\"post\">\n"
"<table class=\"formtable\">\n",
sUrl?sUrl:"");
_addLabelAndInputToFormTableH("Alias", "alias", sAlias, "required", dbuf);
_addLabelAndInputToFormTableH("Name", "name", u?AQH_User_GetName(u):NULL, NULL, dbuf);
_addLabelAndInputToFormTableH("Email", "email", u?AQH_User_GetEmail(u):NULL, NULL, dbuf);
_addLabelAndInputToFormTableH("Notes", "notes", u?AQH_User_GetNotes(u):NULL, NULL, dbuf);
_addUserStateLabelAndSelectionToFormTableH("Status", "status", u?AQH_User_GetState(u):AQH_UserState_Unknown, dbuf);
GBAS(dbuf, "</table>\n");
/* module permissions */
GBAS(dbuf, "<h2>Module Roles</h2>\n");
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>"
"<tr><th>Module</th><th>Roles</th></tr>\n"
"</thead>\n"
"<tbody>\n");
moduleList=AQH_ModService_LoadRawModules(m);
if (moduleList) {
const AQH_MODULE_PERMS_LIST *modPermsList;
const AQH_MODULE *currentMod;
modPermsList=u?AQH_User_GetModulePermList(u):NULL;
currentMod=AQH_Module_List_First(moduleList);
while(currentMod) {
const char *sModName;
const AQH_MODULE_PERMS *modPerms;
sModName=AQH_Module_GetName(currentMod);
GBAA(dbuf, "<tr><td>%s</td><td>", sModName);
modPerms=modPermsList?AQH_ModulePerms_List_GetByModuleId(modPermsList, sModName):NULL;
_writeUserModRolesToForm(AQH_Module_GetRoleList(currentMod), modPerms, sModName, dbuf);
GBAS(dbuf, "</td></tr>\n");
GBAS(dbuf, "<td>");
currentMod=AQH_Module_List_Next(currentMod);
} /* while */
AQH_Module_List_free(moduleList);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"alias\" value=\"%s\">\n", sAlias?sAlias:"");
GBAA(dbuf, "<input type=\"submit\" value=\"%s\">\n</form>\n\n", sSubmitText?sSubmitText:"Save");
}
void _readAllModRolesForUserFromForm(AQH_MODULE *m, GWEN_DB_NODE *dbPost, AQH_USER *u)
{
AQH_MODULE_LIST *moduleList;
moduleList=AQH_ModService_LoadRawModules(m);
if (moduleList) {
AQH_MODULE_PERMS_LIST *permsList;
const AQH_MODULE *module;
permsList=AQH_User_GetModulePermList(u);
if (permsList==NULL) {
DBG_ERROR(NULL, "Creating module perms list for user");
permsList=AQH_ModulePerms_List_new();
AQH_User_SetModulePermList(u, permsList);
}
module=AQH_Module_List_First(moduleList);
while(module) {
const char *sModName;
const AQH_ROLE_LIST *roleList;
sModName=AQH_Module_GetName(module);
roleList=AQH_Module_GetRoleList(module);
if (sModName && *sModName && roleList) {
AQH_MODULE_PERMS *modPerms;
modPerms=AQH_ModulePerms_List_GetByModuleId(permsList, sModName);
if (modPerms==NULL) {
modPerms=AQH_ModulePerms_new();
AQH_ModulePerms_SetModuleId(modPerms, sModName);
AQH_ModulePerms_List_Add(modPerms, permsList);
}
_readModRolesFromForm(dbPost, roleList, sModName, modPerms);
}
module=AQH_Module_List_Next(module);
}
AQH_Module_List_free(moduleList);
}
}
void _writeUserModRolesToForm(const AQH_ROLE_LIST *roleList, const AQH_MODULE_PERMS *modPerms, const char *sModName, GWEN_BUFFER *dbuf)
{
if (roleList) {
const AQH_ROLE *role;
role=AQH_Role_List_First(roleList);
while(role) {
const char *sRoleName;
uint8_t roleId;
roleId=AQH_Role_GetId(role);
sRoleName=AQH_Role_GetName(role);
if (sRoleName && *sRoleName) {
int isChecked;
isChecked=(modPerms && _modulePermsHasRole(modPerms, roleId));
if (sModName && *sModName) {
GBAA(dbuf, "<input type=\"checkbox\" name=\"%s:%s\" %s>", sModName, sRoleName, isChecked?"checked":"");
GBAA(dbuf, "<label for=\"%s:%s\">%s</label>", sModName, sRoleName, sRoleName);
}
else {
GBAA(dbuf, "<input type=\"checkbox\" name=\"%s\" %s>", sRoleName, isChecked?"checked":"");
GBAA(dbuf, "<label for=\"%s\">%s</label>", sRoleName, sRoleName);
}
}
role=AQH_Role_List_Next(role);
}
}
}
void _readModRolesFromForm(GWEN_DB_NODE *dbPost,
const AQH_ROLE_LIST *roleList,
const char *sPrefix,
AQH_MODULE_PERMS *modPerms)
{
AQH_ModulePerms_PresetRoleArray(modPerms, 0);
if (roleList) {
GWEN_BUFFER *tbuf;
uint32_t pos;
const AQH_ROLE *role;
int nextRolePos=0;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
if (sPrefix && *sPrefix)
GBAA(tbuf, "%s:", sPrefix);
pos=GWEN_Buffer_GetPos(tbuf);
role=AQH_Role_List_First(roleList);
while(role) {
const char *roleName;
roleName=AQH_Role_GetName(role);
if (roleName && *roleName) {
const char *s;
GBAS(tbuf, roleName);
s=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(tbuf), 0, NULL);
if (s && *s) {
if (nextRolePos<AQH_ModulePerms_GetRoleArrayArraySize())
AQH_ModulePerms_SetRoleArrayAt(modPerms, nextRolePos++, AQH_Role_GetId(role));
}
GWEN_Buffer_Crop(tbuf, 0, pos);
}
role=AQH_Role_List_Next(role);
} /* while */
GWEN_Buffer_free(tbuf);
}
}
void _addUserStateLabelAndSelectionToFormTableH(const char *sTitle, const char *sName, int st, GWEN_BUFFER *dbuf)
{
int i;
GBAA(dbuf, "<tr><td><label for=\"%s\">%s:</label></td>", sName?sName:"", sTitle?sTitle:"");
GBAA(dbuf, "<td><select name=\"%s\">", sName?sName:"");
for(i=AQH_UserState_Unknown; i<=AQH_UserState_Active; i++) {
const char *s;
s=AQH_UserState_toString(i);
GBAA(dbuf, "<option value=\"%s\" %s>%s</option>", s, (i==st)?"selected":"", s);
}
GBAS(dbuf, "</select></td></tr>");
}
void _addLabelAndInputToFormTableH(const char *sTitle, const char *sName, const char *sValue, const char *sExtra, GWEN_BUFFER *dbuf)
{
GBAS(dbuf, "<tr>");
GBAA(dbuf, "<td><label for=\"%s:\">%s</label></td>", sName?sName:"", sTitle?sTitle:"");
GBAA(dbuf, "<td><input type=\"text\" name=\"%s\"", sName?sName:"");
if (sValue && *sValue)
GBAA(dbuf, " value=\"%s\"", sValue);
if (sExtra && *sExtra)
GBAA(dbuf, " %s", sExtra);
GBAS(dbuf, "></td>");
GBAS(dbuf, "</tr>\n");
}

View File

@@ -0,0 +1,35 @@
/****************************************************************************
* 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_CGI_MUSERS_H
#define AQHOME_CGI_MUSERS_H
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
#define AQH_MODADMUSERS_PERMS_USERSREAD 0x001
#define AQH_MODADMUSERS_PERMS_USERSWRITE 0x002
#define AQH_MODADMUSERS_PERMS_USERSADD 0x004
#define AQH_MODADMUSERS_PERMS_USERSDEL 0x008
void AQH_ModAdmUsers_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
int AQH_ModAdmUsers_Create(AQH_SERVICE *sv);
#endif

View File

@@ -0,0 +1,101 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhcgi_mdevices" >
<includes type="c" >
$(gwenhywfar_cflags)
$(aqcgi_cflags)
$(aqdiagram_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags-INACTIVE" >
--api=AQHOME_API
</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="false" install="$(pkgincludedir)/service" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/service" >
mdevices.h
mdevices_init.h
mdevices_index.h
mdevices_valuestable.h
mdevices_valuesgraph.h
mdevices_value.h
mdevices_setdata.h
mdevices_vgraph.h
mdevices_device.h
mdevices_setdevice.h
mdevices_page.h
</headers>
<headers dist="true" >
</headers>
<sources>
$(local/typefiles)
mdevices.c
mdevices_init.c
mdevices_index.c
mdevices_valuestable.c
mdevices_valuesgraph.c
mdevices_value.c
mdevices_setdata.c
mdevices_vgraph.c
mdevices_device.c
mdevices_setdevice.c
mdevices_page.c
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,565 @@
/****************************************************************************
* 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 "./mdevices.h"
#include "aqhome-cgi/modules/devices/mdevices_index.h"
#include "aqhome-cgi/modules/devices/mdevices_valuestable.h"
#include "aqhome-cgi/modules/devices/mdevices_valuesgraph.h"
#include "aqhome-cgi/modules/devices/mdevices_value.h"
#include "aqhome-cgi/modules/devices/mdevices_setdata.h"
#include "aqhome-cgi/modules/devices/mdevices_vgraph.h"
#include "aqhome-cgi/modules/devices/mdevices_device.h"
#include "aqhome-cgi/modules/devices/mdevices_setdevice.h"
#include "aqhome-cgi/modules/devices/mdevices_page.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
#define P_DEVICEREAD AQH_MODDEVICES_PERMS_DEVICEREAD
#define P_DEVICEWRITE AQH_MODDEVICES_PERMS_DEVICEWRITE
#define P_VALUEREAD AQH_MODDEVICES_PERMS_VALUEREAD
#define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqValuesTableGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqValuesGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqValueGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* vars
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
{"index.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD, _handleRqIndexGet},
{"device.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqDeviceGet},
{"device.html", AQCGI_REQUEST_METHOD_POST, P_DEVICEWRITE, _handleRqDevicePost},
{"vtable.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesTableGet},
{"vgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValuesGraphGet},
{"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet},
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
{"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet},
{"page.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGet},
{"page.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqPagePost},
{"pgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGraphGet},
{NULL, 0, 0, NULL}
};
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
AQH_ModService_HandleRequestWithTable(m, rq, session, sLastPathElem, _requestTable);
return AQCGI_SendResponse(rq);
}
void _handleRqIndexGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunIndex, dbuf);
}
void _handleRqValuesTableGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValuesAsTable, dbuf);
}
void _handleRqValuesGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValuesAsGraph, dbuf);
}
void _handleRqValueGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunValue, dbuf);
}
void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunSetData, dbuf);
}
void _handleRqGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunGraphValue, dbuf);
}
void _handleRqDeviceGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunDevice, dbuf);
}
void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunSetDevice, dbuf);
}
void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGet, dbuf);
}
void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPagePost, dbuf);
}
void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
{
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGraph, dbuf);
}
uint32_t AQH_ModDevices_ColorFromHexString(const char *s)
{
uint32_t colorIn=0;
while(*s && *s<33)
s++;
if (*s=='#')
s++;
while(*s) {
uint c;
c=(*s)-'0';
if (c>9)
c-=7;
colorIn<<=4;
colorIn|=c & 0xf;
s++;
}
/* hex 00RRGGBB -> GGRRWWBB */
//return _htmlColorToValueRGBW(colorIn);
return colorIn;
}
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn)
{
uint32_t colorOut;
/* hex 00RRGGBB -> GGRRWWBB */
/* RGBW GGRRWWBB GGRRWWBB GGRRWWBB */
/* html 00RRGGBB 00RRGGBB 00RRGGBB*/
colorOut=(colorIn & 0x00ff0000) | ((colorIn<<16) & 0xff000000) | (colorIn & 0x000000ff);
return colorOut;
}
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn)
{
uint32_t colorOut;
/* RGBW GGRRWWBB GGRRWWBB GGRRWWBB */
/* hex 00RRGGBB 00RRGGBB 00RRGGBB*/
colorOut=(colorIn & 0x00ff0000) | ( (colorIn>>16) & 0x0000ff00) | (colorIn & 0x000000ff);
return colorOut;
}
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w)
{
uint32_t colorOut;
colorOut=((r & 0xff)<<16) | ((g & 0xff)<<24) | (b & 0xff) | ((w & 0xff)<<8);
return colorOut;
}
int AQH_ModDevices_RgbwGetR(uint32_t color)
{ /* GGRRWWBB */
return (color & 0x00ff0000)>>16;
}
int AQH_ModDevices_RgbwGetG(uint32_t color)
{ /* GGRRWWBB */
return (color & 0xff000000)>>24;
}
int AQH_ModDevices_RgbwGetB(uint32_t color)
{ /* GGRRWWBB */
return (color & 0x000000ff);
}
int AQH_ModDevices_RgbwGetW(uint32_t color)
{ /* GGRRWWBB */
return (color & 0x0000ff00)>>8;
}
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueName;
sValueName=AQH_Value_GetName(value);
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW:
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"refresh\"/>", sValueName);
break;
case AQH_ValueModality_OnOff:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"</select>",
sValueName);
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf,
"<select name=\"%s\">"
"<option value=\"unchanged\">unchanged</option>"
"<option value=\"off\">off</option>"
"<option value=\"on\">on</option>"
"<option value=\"auto\">auto</option>"
"</select>",
sValueName);
break;
default:
break;
}
}
void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueSystemName;
const char *sValueName;
uint64_t dataPoints[2];
uint64_t recvdNum;
// uint64_t timestamp;
union {double f; uint64_t i;} u;
int intVal;
sValueSystemName=AQH_Value_GetNameForSystem(value);
sValueName=AQH_Value_GetName(value);
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
if (recvdNum>0) {
// timestamp=dataPoints[0];
u.i=dataPoints[1];
intVal=(int) u.f;
}
else {
u.i=0;
intVal=-1;
}
if (AQH_Value_GetValueType(value)==AQH_ValueType_Actor) {
DBG_ERROR(NULL, "Adding actor");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW:
DBG_ERROR(NULL, "Color: %.f RGBW=%08x HTML=%08x, RGBW2=%08x",
u.f,
(uint32_t) (u.f),
AQH_ModDevices_RgbwToHtmlColor(u.f),
AQH_ModDevices_HtmlColorToValueRGBW(AQH_ModDevices_RgbwToHtmlColor(u.f)));
#if 1
GBAA(dbuf, "<input type=\"text\" name=\"%s\" value=\"#%08x\"/>", sValueName, (uint32_t) (u.f));
#else
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"#%08x\"/>#%08x (#%08x)",
sValueName,
_rgbwToHtmlColor((unsigned int) (u.f)),
_rgbwToHtmlColor((unsigned int) (u.f)),
(uint32_t) (u.f));
#endif
break;
case AQH_ValueModality_OnOff:
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
break;
case AQH_ValueModality_OnOffAuto:
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
break;
default:
// GBAA(dbuf, "<input type=\"text\" name=\"%s\" value=\"%.2f\"/>", sValueName, u.f);
GBAA(dbuf, "%.2f", u.f);
break;
} /* switch */
} /* if actor */
else {
DBG_ERROR(NULL, "Adding sensor (%s=%.2f)", sValueName, u.f);
GBAA(dbuf, "%.2f", u.f);
}
}
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName)
{
AQH_VALUE_LIST *valueList;
valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0);
if (valueList) {
AQH_VALUE *value;
value=AQH_Value_List_First(valueList);
while(value) {
const char *s;
s=AQH_Value_GetName(value);
if (s && *s && strcasecmp(s, sValueName)==0) {
break;
}
value=AQH_Value_List_Next(value);
}
if (value) {
AQH_Value_List_Del(value);
AQH_Value_List_free(valueList);
return value;
}
AQH_Value_List_free(valueList);
}
return NULL;
}
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName)
{
AQH_DEVICE_LIST *deviceList;
deviceList=AQH_DataClient_GetDevices(dc, sDeviceName);
if (deviceList) {
AQH_DEVICE *device;
device=AQH_Device_List_First(deviceList);
while(device) {
const char *s;
s=AQH_Device_GetNameForSystem(device);
if (s && *s && 0==strcasecmp(s, sDeviceName)) {
AQH_Device_List_Del(device);
AQH_Device_List_free(deviceList);
return device;
}
device=AQH_Device_List_Next(device);
}
AQH_Device_List_free(deviceList);
}
return NULL;
}
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue)
{
const char *sValueSystemName;
uint64_t dataPoints[2];
uint64_t recvdNum;
// uint64_t timestamp;
union {double f; uint64_t i;} u;
int intVal;
sValueSystemName=AQH_Value_GetNameForSystem(value);
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
if (recvdNum>0) {
// timestamp=dataPoints[0];
u.i=dataPoints[1];
intVal=(int) u.f;
}
else {
DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName);
intVal=defaultValue;
}
return intVal;
}
uint32_t AQH_ModDevices_ValueGetLastDataAsUint32(AQH_DATACLIENT *dc, const AQH_VALUE *value, uint32_t defaultValue)
{
const char *sValueSystemName;
uint64_t dataPoints[2];
uint64_t recvdNum;
// uint64_t timestamp;
union {double f; uint64_t i;} u;
uint32_t uintVal;
sValueSystemName=AQH_Value_GetNameForSystem(value);
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
if (recvdNum>0) {
// timestamp=dataPoints[0];
u.i=dataPoints[1];
uintVal=(uint32_t) u.f;
DBG_ERROR(NULL, "Transformed value %.2f -> %08x", u.f, uintVal);
}
else {
DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName);
uintVal=defaultValue;
}
return uintVal;
}
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
{
uint64_t *dataPoints;
uint64_t recvdNum;
dataPoints=malloc(num*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetPeriodData(dc, systemValueName, dataPoints, num, tsBegin, tsEnd);
if (recvdNum>0) {
AQDG_GRAPH_DATAPAIR_LIST *dpList;
dpList=_createDataPairListFromDataPoints(dataPoints, recvdNum);
free(dataPoints);
return dpList;
}
else {
DBG_ERROR(NULL, "No data received for %s", systemValueName);
free(dataPoints);
return NULL;
}
}
AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues)
{
AQDG_GRAPH_DATAPAIR_LIST *dpList;
uint64_t i;
DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues);
dpList=AQDG_Graph_DataPair_List_new();
for(i=0; i<numValues; i++) {
AQDG_GRAPH_DATAPAIR *dp;
double timestamp;
union {double f; uint64_t i;} u;
timestamp=(double)(*(dataPoints++));
u.i=*(dataPoints++);
dp=AQDG_Graph_DataPair_new();
AQDG_Graph_DataPair_SetValueX(dp, timestamp);
AQDG_Graph_DataPair_SetValueY(dp, u.f);
AQDG_Graph_DataPair_List_Add(dp, dpList);
}
AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
return dpList;
}

View File

@@ -0,0 +1,71 @@
/****************************************************************************
* 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_CGI_MODULES_DEVICES_H
#define AQHOME_CGI_MODULES_DEVICES_H
#include "aqhome-cgi/modules/mdataclient.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <aqdiagram/graph/datapair.h>
#include <gwenhywfar/buffer.h>
#define AQH_MODDEVICES_PERMS_DEVICEREAD 0x001
#define AQH_MODDEVICES_PERMS_DEVICEWRITE 0x002
#define AQH_MODDEVICES_PERMS_DEVICEADD 0x004
#define AQH_MODDEVICES_PERMS_DEVICEDEL 0x008
#define AQH_MODDEVICES_PERMS_VALUEREAD 0x010
#define AQH_MODDEVICES_PERMS_VALUEWRITE 0x020
#define AQH_MODDEVICES_PERMS_VALUEADD 0x040
#define AQH_MODDEVICES_PERMS_VALUEDEL 0x080
#define AQH_MODDEVICES_PERMS_VALUESET 0x100
#define AQH_MODDEVICES_GRAPH_WIDTH 640
#define AQH_MODDEVICES_GRAPH_HEIGHT 480
void AQH_ModDevices_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
int AQH_ModDevices_Create(AQH_SERVICE *sv);
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn);
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w);
int AQH_ModDevices_RgbwGetR(uint32_t color);
int AQH_ModDevices_RgbwGetG(uint32_t color);
int AQH_ModDevices_RgbwGetB(uint32_t color);
int AQH_ModDevices_RgbwGetW(uint32_t color);
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName);
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName);
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue);
uint32_t AQH_ModDevices_ValueGetLastDataAsUint32(AQH_DATACLIENT *dc, const AQH_VALUE *value, uint32_t defaultValue);
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
#endif

View File

@@ -0,0 +1,131 @@
/****************************************************************************
* 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 "./mdevices_device.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
#define I18N(msg) msg
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runDeviceWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_DATACLIENT *dc,
const char *sDeviceName,
GWEN_BUFFER *dbuf);
static void _mkDeviceForm(AQH_DATACLIENT *dc, const char *sDeviceName, const AQH_DEVICE *device, GWEN_BUFFER *dbuf);
static void _addFieldToForm(const char *sFieldTitle, const char *sFieldName, const char *sFieldContent, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunDevice(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
DBG_ERROR(NULL, "RunValue");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
if (sDeviceName && *sDeviceName) {
GWEN_BUFFER *bufDeviceName;
bufDeviceName=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Text_UnescapeToBufferTolerant(sDeviceName, bufDeviceName);
_runDeviceWithArgs(m, rq, session, dc, GWEN_Buffer_GetStart(bufDeviceName), dbuf);
GWEN_Buffer_free(bufDeviceName);
}
}
void _runDeviceWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_DATACLIENT *dc,
const char *sDeviceName,
GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
AQH_DEVICE *device;
dbQuery=AQCGI_Request_GetDbQuery(rq);
DBG_ERROR(NULL, "Device=%s", sDeviceName?sDeviceName:"<empty>");
GBAA(dbuf,"<h1>Device %s</h1>\n", sDeviceName);
device=AQH_ModDevices_GetDevice(dc, sDeviceName);
if (device) {
_mkDeviceForm(dc, sDeviceName, device, dbuf);
AQH_Device_free(device);
}
}
void _mkDeviceForm(AQH_DATACLIENT *dc, const char *sDeviceName, const AQH_DEVICE *device, GWEN_BUFFER *dbuf)
{
const char *s;
GBAS(dbuf,"<form action=\"device.html\" method=\"post\">\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"device\" value=\"%s\">\n", sDeviceName);
GBAS(dbuf,"<table>\n");
_addFieldToForm(I18N("Room"), "roomName", AQH_Device_GetRoomName(device), dbuf);
_addFieldToForm(I18N("GUI Name"), "nameForGui", AQH_Device_GetNameForGui(device), dbuf);
_addFieldToForm(I18N("Location"), "location", AQH_Device_GetLocation(device), dbuf);
_addFieldToForm(I18N("Description"), "description", AQH_Device_GetDescription(device), dbuf);
GBAS(dbuf,"</table>\n");
GBAS(dbuf,"<br>\n");
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
GBAS(dbuf, "</form>\n\n");
}
void _addFieldToForm(const char *sFieldTitle, const char *sFieldName, const char *sFieldContent, GWEN_BUFFER *dbuf)
{
GBAS(dbuf, "<tr>");
GBAA(dbuf, "<td><label for=\"%s\">%s</label></td>", sFieldName, sFieldTitle);
GBAA(dbuf, "<td><input type=\"text\" name=\"%s\" value=\"%s\"/></td>", sFieldName, sFieldContent?sFieldContent:"");
GBAS(dbuf, "</tr>");
}

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_CGI_MDEVICES_DEVICE_H
#define AQHOME_CGI_MDEVICES_DEVICE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunDevice(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,135 @@
/****************************************************************************
* 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 "./mdevices_index.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _addLinkForDevice(const char *page, const char *sDevice, const char *action, const char *imgName, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
AQH_DEVICE_LIST *deviceList;
AQH_DEVICE *device;
uint32_t perms;
perms=AQH_ModService_GetUserPerms(m);
deviceList=AQH_DataClient_GetDevices(dc, NULL);
if (deviceList==NULL) {
DBG_ERROR(NULL, "No device received");
GBAS(dbuf, "<p>No devices.</p>");
return;
}
GBAS(dbuf, "<h1>Devices</h1>\n");
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>\n"
"<tr>"
"<th>Name For System</th>"
"<th>Name For GUI</th>"
"<th>Room</th>"
"<th>Location</th>"
"<th>Description</th>"
"<th>Actions</th>"
"</tr>\n"
"</thead>\n"
"<tbody>\n");
device=AQH_Device_List_First(deviceList);
while(device) {
const char *s;
const char *sDevice;
GBAA(dbuf, "<tr>");
/* name for system */
sDevice=AQH_Device_GetNameForSystem(device);
GBAA(dbuf,"<td>%s</td>", sDevice?sDevice:"");
/* nameForGui */
s=AQH_Device_GetNameForGui(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* room */
s=AQH_Device_GetRoomName(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* location */
s=AQH_Device_GetLocation(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
/* description */
s=AQH_Device_GetDescription(device);
GBAA(dbuf, "<td>%s</td>", s?s:"");
GBAS(dbuf, "<td>");
if (perms & AQH_MODDEVICES_PERMS_VALUEREAD) {
_addLinkForDevice("vtable.html", sDevice, "table view", "/pics/document-table.png", dbuf);
_addLinkForDevice("vgraph.html", sDevice, "graph view", "/pics/graph.png", dbuf);
}
if (perms & AQH_MODDEVICES_PERMS_DEVICEWRITE) {
_addLinkForDevice("device.html", sDevice, "edit device", "/pics/edit.png", dbuf);
}
GBAS(dbuf, "</td>");
GBAA(dbuf, "</tr>");
device=AQH_Device_List_Next(device);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
AQH_Device_List_free(deviceList);
}
void _addLinkForDevice(const char *page, const char *sDevice, const char *action, const char *imgName, GWEN_BUFFER *dbuf)
{
GBAA(dbuf,"<a href=\"%s?device=", page);
GWEN_Text_EscapeToBufferTolerant(sDevice, dbuf);
GBAS(dbuf,"\">");
GBAA(dbuf,"<img src=\"%s\" alt=\"%s\" title=\"%s\" />", imgName, action, action);
GBAS(dbuf,"</a>");
}

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_CGI_MDEVICES_INDEX_H
#define AQHOME_CGI_MDEVICES_INDEX_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,129 @@
/****************************************************************************
* 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 "./mdevices_init.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
#define P_DEVICEREAD AQH_MODDEVICES_PERMS_DEVICEREAD
#define P_VALUEREAD AQH_MODDEVICES_PERMS_VALUEREAD
#define P_VALUEWRITE AQH_MODDEVICES_PERMS_VALUEWRITE
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _createPermDefList(AQH_MODULE *m);
static void _createRoleList(AQH_MODULE *m);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_ModDevices_Create(AQH_SERVICE *sv)
{
AQH_MODULE *m;
int rv;
m=AQH_Module_new();
AQH_Module_SetName(m, "devices");
AQH_Module_SetDescr(m, "device module");
AQH_Module_SetGuestPerms(m, 0);
_createPermDefList(m);
_createRoleList(m);
rv=AQH_Service_AddModule(sv, m);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Module_free(m);
return rv;
}
void _createPermDefList(AQH_MODULE *m)
{
AQH_PERMDEF_LIST *permDefList;
permDefList=AQH_PermDef_List_new();
AQH_ModService_AddPermDef(permDefList, "DeviceRead", 0x001, "Read and list devices");
AQH_ModService_AddPermDef(permDefList, "DeviceWrite", 0x002, "Modify devices");
AQH_ModService_AddPermDef(permDefList, "DeviceAdd", 0x004, "Add devices");
AQH_ModService_AddPermDef(permDefList, "DeviceDel", 0x008, "Remove devices");
AQH_ModService_AddPermDef(permDefList, "ValueRead", 0x010, "Read and list values");
AQH_ModService_AddPermDef(permDefList, "ValueWrite", 0x020, "Modify values");
AQH_ModService_AddPermDef(permDefList, "ValueAdd", 0x040, "Add values");
AQH_ModService_AddPermDef(permDefList, "ValueDel", 0x080, "Remove values");
AQH_ModService_AddPermDef(permDefList, "ValueSet", 0x100, "Set values");
AQH_Module_SetPermDefList(m, permDefList);
}
void _createRoleList(AQH_MODULE *m)
{
AQH_ROLE_LIST *roleList;
int id=0;
roleList=AQH_Role_List_new();
AQH_ModService_AddRole(roleList, id++, "Reader",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_VALUEREAD,
"Read devices and values");
AQH_ModService_AddRole(roleList, id++, "Writer",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_DEVICEWRITE |
AQH_MODDEVICES_PERMS_DEVICEADD |
AQH_MODDEVICES_PERMS_DEVICEDEL |
AQH_MODDEVICES_PERMS_VALUEREAD |
AQH_MODDEVICES_PERMS_VALUEWRITE |
AQH_MODDEVICES_PERMS_VALUEADD |
AQH_MODDEVICES_PERMS_VALUEDEL |
AQH_MODDEVICES_PERMS_VALUESET,
"Read and write devices and values");
AQH_ModService_AddRole(roleList, id++, "Setter",
AQH_MODDEVICES_PERMS_DEVICEREAD |
AQH_MODDEVICES_PERMS_VALUEREAD |
AQH_MODDEVICES_PERMS_VALUESET,
"Set values");
AQH_Module_SetRoleList(m, roleList);
}

View File

@@ -1,28 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
* 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 AQH_MSG_NEEDADDR_H
#define AQH_MSG_NEEDADDR_H
#ifndef AQHOME_CGI_MDEVICES_INIT_H
#define AQHOME_CGI_MDEVICES_INIT_H
#include <aqhome/api.h>
#include <aqhome/msg/msg_node.h>
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/msg.h>
#include <gwenhywfar/buffer.h>
AQHOME_API uint32_t AQH_NeedAddrMsg_GetUid(const GWEN_MSG *msg);
AQHOME_API void AQH_NeedAddrMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText);
int AQH_ModDevices_Create(AQH_SERVICE *sv);
#endif

View File

@@ -0,0 +1,961 @@
/****************************************************************************
* 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 "./mdevices_page.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <aqdiagram/graph/timegraph.h>
#include <aqdiagram/graph/w_graph.h>
#include <aqdiagram/draw/context_cairo.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/directory.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
enum {
MY_LAYOUT_NONE=0,
MY_LAYOUT_HORIZONTAL,
MY_LAYOUT_VERTICAL
};
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf);
static void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf);
static void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
static void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
static void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor);
static void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf);
static void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, const char *sFilename, GWEN_XMLNODE *nGraph);
static void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd);
static AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd);
static uint64_t _parseTime(const char *s);
static GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName);
static void _mkPathForGraph(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *dbuf);
static void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf);
static void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf);
static void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
static void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
static void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value);
static int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue);
static void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
static void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
static void _sendPageList(AQH_MODULE *m, GWEN_BUFFER *dbuf);
static GWEN_STRINGLIST *_listPageFiles(AQH_MODULE *m);
static GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName);
static GWEN_XMLNODE *_readPageFile(const char *sFilename);
static int _layoutFromString(const char *s);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sPageId;
DBG_INFO(NULL, "RunPageGet");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sPageId=dbQuery?GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL):NULL;
if (sPageId && *sPageId) {
GWEN_XMLNODE *fileNode;
fileNode=_readPage(m, sPageId);
if (fileNode) {
GWEN_XMLNODE *nPage;
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", NULL, NULL);
if (nPage) {
_writePage(m, dc, nPage, dbuf);
AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120");
}
else {
DBG_ERROR(NULL, "No page element in file for \"%s\"", sPageId);
}
GWEN_XMLNode_free(fileNode);
}
else {
DBG_INFO(NULL, "here");
}
}
else {
DBG_ERROR(NULL, "Reading page list");
_sendPageList(m, dbuf);
}
}
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sPageId;
const char *sGraphId;
DBG_INFO(NULL, "RunPageGraphGet");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sPageId=GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL);
sGraphId=GWEN_DB_GetCharValue(dbQuery, "graph", 0, NULL);
if (sPageId && *sPageId && sGraphId && *sGraphId) {
GWEN_XMLNODE *fileNode;
fileNode=_readPage(m, sPageId);
if (fileNode) {
GWEN_XMLNODE *nPage;
GWEN_XMLNODE *nGraph;
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId);
nGraph=_getSubItemNode(nPage, sGraphId, "graph");
if (nPage && nGraph)
_handlePageGraph(m, rq, dc, sGraphId, nGraph, dbuf);
else {
DBG_ERROR(NULL, "Graph %s/%s not found", sPageId, sGraphId);
}
GWEN_XMLNode_free(fileNode);
}
else {
DBG_INFO(NULL, "here");
}
}
}
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbPost;
const char *sPageId;
const char *sActorId;
DBG_INFO(NULL, "RunPagePost");
dbPost=AQCGI_Request_GetDbPostBody(rq);
sPageId=GWEN_DB_GetCharValue(dbPost, "page", 0, NULL);
sActorId=GWEN_DB_GetCharValue(dbPost, "actor", 0, NULL);
if (sPageId && *sPageId && sActorId && *sActorId) {
GWEN_XMLNODE *fileNode;
fileNode=_readPage(m, sPageId);
if (fileNode) {
GWEN_XMLNODE *nPage;
GWEN_XMLNODE *nActor;
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId);
nActor=_getSubItemNode(nPage, sActorId, "actor");
if (nPage && nActor) {
_handlePageActor(rq, dc, sActorId, nActor);
}
else {
DBG_ERROR(NULL, "Actor %s/%s not found", sPageId, sActorId);
}
GWEN_XMLNode_free(fileNode);
}
else {
DBG_INFO(NULL, "here");
}
}
if (sPageId && *sPageId) {
GWEN_BUFFER *pbuf;
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAS(pbuf, "Location: page.html?page=");
GWEN_Text_EscapeToBuffer(sPageId, pbuf);
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
GWEN_Buffer_free(pbuf);
}
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor)
{
GWEN_DB_NODE *dbPost;
const char *sDeviceName;
const char *sValueName;
dbPost=AQCGI_Request_GetDbPostBody(rq);
sDeviceName=GWEN_XMLNode_GetProperty(nActor, "device", NULL);
sValueName=GWEN_XMLNode_GetProperty(nActor, "value", NULL);
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
AQH_VALUE *value;
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
if (value) {
const char *sSystemValueName;
sSystemValueName=AQH_Value_GetNameForSystem(value);
if (sSystemValueName) {
const char *sData;
sData=GWEN_DB_GetCharValue(dbPost, sActorId, 0, NULL);
DBG_INFO(NULL, "Setting value %s to %s", sSystemValueName, sData?sData:"empty/no value");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW: _setRgbwData(dc, dbPost, sActorId, value); break;
case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sData); break;
case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sData); break;
default:
break;
} /* switch */
}
}
}
}
void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf)
{
GWEN_BUFFER *fbuf;
int refreshTime;
refreshTime=GWEN_XMLNode_GetIntProperty(nGraph, "refreshTime", 120);
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
_mkPathForGraph(m, sGraphId, fbuf);
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), refreshTime)) {
_genPageGraph(dc, sGraphId, GWEN_Buffer_GetStart(fbuf), nGraph);
}
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
GWEN_Buffer_free(fbuf);
}
void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, const char *sFilename, GWEN_XMLNODE *nGraph)
{
const char *s;
const char *sTitle;
int w;
int h;
int precision;
uint64_t tsBegin;
uint64_t tsEnd;
AQDG_GRAPH *g;
AQDG_DRAW_CONTEXT *drawContext;
AQDG_OBJECT *graphObject;
uint32_t tickFlags=0;
double upperLimit;
double lowerLimit;
sTitle=GWEN_XMLNode_GetProperty(nGraph, "title", "untitled");
w=GWEN_XMLNode_GetIntProperty(nGraph, "width", AQH_MODDEVICES_GRAPH_WIDTH);
h=GWEN_XMLNode_GetIntProperty(nGraph, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
precision=GWEN_XMLNode_GetIntProperty(nGraph, "precision", 2);
tsBegin=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "begin", "-4h"));
tsEnd=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "end", "0"));
s=GWEN_XMLNode_GetProperty(nGraph, "lowerLimit", NULL);
if (s && *s) {
if (1==sscanf(s, "%lf", &lowerLimit))
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MINY;
else {
DBG_ERROR(NULL, "Ignoring invalid lowerLimit (%s)", s);
}
}
s=GWEN_XMLNode_GetProperty(nGraph, "upperLimit", NULL);
if (s && *s) {
if (1==sscanf(s, "%lf", &upperLimit))
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MAXY;
else {
DBG_ERROR(NULL, "Ignoring invalid upperLimit (%s)", s);
}
}
g=AQDG_TimeGraph_new(sTitle, NULL, "Value", NULL, precision);
_addCurves(dc, g, nGraph, tsBegin, tsEnd);
AQDG_TimeGraph_SetupTicks(g, tickFlags, lowerLimit, upperLimit);
DBG_DEBUG(NULL, "Draw graph for %s", sGraphId);
drawContext=AQDG_Draw_ContextCairo_Png_new(sFilename, w, h);
graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
AQDG_Object_SetWidth(graphObject, w);
AQDG_Object_SetHeight(graphObject, h);
AQDG_GraphWidget_SetupDefaultPens(graphObject);
AQDG_GraphWidget_SetupDefaultFonts(graphObject);
AQDG_GraphWidget_FinishWithGraph(graphObject, g);
AQDG_Object_free(graphObject);
}
void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd)
{
GWEN_XMLNODE *nCurve;
nCurve=GWEN_XMLNode_FindFirstTag(nGraph, "curve", NULL, NULL);
while(nCurve) {
const char *sModifier;
sModifier=GWEN_XMLNode_GetProperty(nCurve, "modifier", NULL);
if (sModifier && *sModifier) {
AQDG_GRAPH_DATAPAIR_LIST *dpList;
dpList=_readCurveData(dc, nCurve, tsBegin, tsEnd);
if (dpList) {
const char *sCurveLabel;
sCurveLabel=GWEN_XMLNode_GetProperty(nCurve, "title", NULL);
DBG_DEBUG(NULL, "Adding data for %s", sCurveLabel?sCurveLabel:"<no title>");
AQDG_TimeGraph_ModifyDataAndAddCurve(g, sCurveLabel?sCurveLabel:"<no title>", sModifier, dpList);
}
}
nCurve=GWEN_XMLNode_FindNextTag(nCurve, "curve", NULL, NULL);
}
}
AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd)
{
const char *sDeviceName;
const char *sValueName;
sDeviceName=GWEN_XMLNode_GetProperty(nCurve, "device", NULL);
sValueName=GWEN_XMLNode_GetProperty(nCurve, "value", NULL);
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
AQDG_GRAPH_DATAPAIR_LIST *dpList;
GWEN_BUFFER *vbuf;
vbuf=GWEN_Buffer_new(0, 64, 0, 1);
GBAA(vbuf, "%s/%s", sDeviceName, sValueName);
dpList=AQH_ModDevices_RequestDataPairList(dc, GWEN_Buffer_GetStart(vbuf), tsBegin, tsEnd, 100000);
if (dpList) {
GWEN_Buffer_free(vbuf);
return dpList;
}
GWEN_Buffer_free(vbuf);
}
return NULL;
}
// TODO: move to aqhome.{c,h}
uint64_t _parseTime(const char *s)
{
if (s && *s) {
if (*s=='-') {
uint64_t x=0;
uint64_t now=time(NULL);
s++;
while(*s && isdigit(*s)) {
unsigned int i;
i=*(s++)-'0';
x*=10;
x+=i;
}
if (*s) {
switch(*s) {
case 0:
case 'm': x*=60; break;
case 'h': x*=(60*60); break;
case 'd': x*=(60*60*24); break;
case 'w': x*=(60*60*24*7); break;
case 'M': x*=(60*60*24*30); break;
case 'y': x*=(60*60*24*365); break;
default: break;
}
}
return (now-x);
}
if (*s=='@') {
int y, m, d, H, M, S;
if (6==sscanf(s+1, "%d/%d/%d-%d:%d:%d", &y, &m, &d, &H, &M, &S)) {
GWEN_TIMESTAMP *ts;
uint64_t x=0;
ts=GWEN_Timestamp_new(y, m, d, H, M, S);
x=GWEN_Timestamp_toTimeT(ts);
GWEN_Timestamp_free(ts);
return x;
}
else {
DBG_ERROR(NULL, "Invalid timespec [%s], expected: @YYYY/MM/DD-HH:MM:SS", s);
return (uint64_t) (-1);
}
}
else {
unsigned long int x;
if (1!=sscanf(s, "%lu", &x)) {
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
return (uint64_t) (-1);
}
return (uint64_t) x;
}
}
return 0;
}
GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName)
{
GWEN_XMLNODE *nItem;
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
while(nItem) {
GWEN_XMLNODE *nGraph;
nGraph=GWEN_XMLNode_FindFirstTag(nItem, sElementName, "id", sId);
if (nGraph)
return nGraph;
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
}
return NULL;
}
void _mkPathForGraph(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *buf)
{
AQH_SERVICE *sv;
const char *s;
sv=AQH_ModService_GetService(m);
s=AQH_Service_GetCacheFolder(sv);
GBAA(buf, "%s%s%s", s, GWEN_DIR_SEPARATOR_S, sGraphId);
AQH_ModService_EscapeToBuffer(s, buf);
GBAS(buf, ".png");
}
void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf)
{
const char *sPageId;
GWEN_XMLNODE *nItem;
int layout;
const char *s;
sPageId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
/* title */
s=GWEN_XMLNode_GetProperty(nPage, "title", NULL);
if (s && *s)
GBAA(dbuf, "<h1>%s</h1>\n", s);
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nPage, "layout", "none"));
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "<table class=\"pageTable\">\n");
if (layout==MY_LAYOUT_HORIZONTAL)
GBAS(dbuf, "<tr>\n");
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
while(nItem) {
if (layout==MY_LAYOUT_VERTICAL)
GBAS(dbuf, "<tr>\n");
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "<td>");
_writeItem(m, dc, sPageId, nItem, dbuf);
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "</td>");
if (layout==MY_LAYOUT_VERTICAL)
GBAS(dbuf, "</tr>\n");
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
} /* while */
if (layout==MY_LAYOUT_HORIZONTAL)
GBAS(dbuf, "</tr>\n");
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "</table> <!-- pageTable -->\n");
}
void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf)
{
GWEN_XMLNODE *n;
uint32_t perms;
int layout;
perms=AQH_ModService_GetUserPerms(m);
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nItem, "layout", "none"));
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "<table class=\"itemTable\">\n");
if (layout==MY_LAYOUT_HORIZONTAL)
GBAS(dbuf, "<tr>\n");
n=GWEN_XMLNode_GetFirstTag(nItem);
while(n) {
const char *sName;
if (layout==MY_LAYOUT_VERTICAL)
GBAS(dbuf, "<tr>\n");
sName=GWEN_XMLNode_GetData(n);
if (sName && *sName) {
if (strcasecmp(sName, "actor")==0) {
if (perms && AQH_MODDEVICES_PERMS_VALUEWRITE)
_writeActor(dc, sPageId, n, layout, dbuf);
else {
DBG_ERROR(NULL, "No permissions to write values");
}
}
else if (strcasecmp(sName, "graph")==0)
_writeGraph(sPageId, n, layout, dbuf);
else {
DBG_ERROR(NULL, "Ignoring element \"%s\"", sName);
}
}
if (layout==MY_LAYOUT_VERTICAL)
GBAS(dbuf, "</tr>\n");
n=GWEN_XMLNode_GetNextTag(n);
}
if (layout==MY_LAYOUT_HORIZONTAL)
GBAS(dbuf, "</tr>\n");
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "</table> <!-- itemTable -->\n");
}
void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
{
const char *sActorId;
const char *sDeviceName;
const char *sValueName;
const char *sLabel;
sActorId=GWEN_XMLNode_GetProperty(n, "id", NULL);
sLabel=GWEN_XMLNode_GetProperty(n, "label", NULL);
sDeviceName=GWEN_XMLNode_GetProperty(n, "device", NULL);
sValueName=GWEN_XMLNode_GetProperty(n, "value", NULL);
if (sActorId && *sActorId && sDeviceName && *sDeviceName && sValueName && *sValueName) {
AQH_VALUE *value;
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
if (value) {
uint32_t lastData;
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "<td>\n");
lastData=AQH_ModDevices_ValueGetLastDataAsUint32(dc, value, 0);
GBAS(dbuf,"<form action=\"page.html\" method=\"post\">\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"page\" value=\"%s\">\n", sPageId);
GBAA(dbuf, "<input type=\"hidden\" name=\"actor\" value=\"%s\">\n", sActorId);
DBG_INFO(NULL, "Adding actor");
if (sLabel && *sLabel)
GBAA(dbuf,"<label for=\"%s\">%s</label>", sActorId, sLabel);
if (layout!=MY_LAYOUT_NONE) {
GBAS(dbuf, "</td>\n");
GBAS(dbuf, "<td>\n");
}
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW: _writeRgbwToForm(sActorId, lastData, dbuf); break;
case AQH_ValueModality_OnOff: _writeOnOffToForm(sActorId, lastData, dbuf); break;
case AQH_ValueModality_OnOffAuto: _writeOnOffAutoToForm(sActorId, lastData, dbuf); break;
default: GBAA(dbuf, "%d", lastData); break;
} /* switch */
if (layout!=MY_LAYOUT_NONE) {
GBAS(dbuf, "</td>\n");
GBAS(dbuf, "<td>\n");
}
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
GBAS(dbuf, "</form>\n\n");
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "</td>\n");
}
}
}
void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
{
const char *sGraphId;
int w;
int h;
w=GWEN_XMLNode_GetIntProperty(n, "width", AQH_MODDEVICES_GRAPH_WIDTH);
h=GWEN_XMLNode_GetIntProperty(n, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
sGraphId=GWEN_XMLNode_GetProperty(n, "id", NULL);
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "<td colspan=\"3\">\n");
if (sGraphId && *sGraphId)
_addGraphLink(sPageId, sGraphId, w, h, dbuf);
if (layout!=MY_LAYOUT_NONE)
GBAS(dbuf, "</td>\n");
}
void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf)
{
GBAS(dbuf, "<img src=\"pgraph.html?page=");
AQH_ModService_EscapeToBuffer(sPageId, dbuf);
GBAS(dbuf, "&graph=");
AQH_ModService_EscapeToBuffer(sGraphId, dbuf);
GBAA(dbuf, "\" alt=\"%s\" width=\"%d\" height=\"%d\"", sGraphId, w, h);
GBAS(dbuf, "/>");
}
void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf)
{
#if 1
DBG_ERROR(NULL, "Color=%08x (%d, %d, %d, %d)",
color,
AQH_ModDevices_RgbwGetR(color),
AQH_ModDevices_RgbwGetG(color),
AQH_ModDevices_RgbwGetB(color),
AQH_ModDevices_RgbwGetW(color));
GBAA(dbuf, "<label for=\"%s_r\">R:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_r\" id=name=\"%s_r\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetR(color));
GBAA(dbuf, "<label for=\"%s_g\">G:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_g\" id=name=\"%s_g\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetG(color));
GBAA(dbuf, "<label for=\"%s_b\">B:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_b\" id=name=\"%s_b\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetB(color));
GBAA(dbuf, "<label for=\"%s_w\">W:</label>", sValueName);
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_w\" id=name=\"%s_w\" value=\"%d\">",
sValueName, sValueName, AQH_ModDevices_RgbwGetW(color));
#else
GBAA(dbuf, "<input type=\"text\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>", sValueName, sValueName, color);
// else
GBAA(dbuf, "<input type=\"color\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>#%08x (#%08x)",
sValueName, sValueName,
AQH_ModDevices_RgbwToHtmlColor(color),
AQH_ModDevices_RgbwToHtmlColor(color),
color);
#endif
}
void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value)
{
const char *sValueSystemName;
uint32_t color;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
DBG_INFO(NULL, "Set value %s", sValueName);
color=AQH_ModDevices_RgbwFromComponents(_getColorComponent(dbPost, sValueName, "r", 0),
_getColorComponent(dbPost, sValueName, "g", 0),
_getColorComponent(dbPost, sValueName, "b", 0),
_getColorComponent(dbPost, sValueName, "w", 0));
DBG_INFO(NULL, "Send value [#%08x] to %s", color, sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, (double) color);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue)
{
GWEN_BUFFER *buf;
const char *sData;
int result;
buf=GWEN_Buffer_new(0, 64, 0, 1);
GBAA(buf, "%s_%s", sValueName, sComponent);
DBG_INFO(NULL, "Read value %s", GWEN_Buffer_GetStart(buf));
sData=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(buf), 0, NULL);
GWEN_Buffer_free(buf);
if (sData) {
if (1==sscanf(sData, "%u", &result))
return result;
}
return defaultValue;
}
void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<select name=\"%s\" id=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
}
void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
if (strcasecmp(sValue, "unchanged")==0) {
DBG_INFO(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
else {
}
}
}
void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<select name=\"%s\" id=\"%s\" >" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
}
void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
if (strcasecmp(sValue, "unchanged")==0) {
DBG_INFO(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "auto")==0) {
DBG_INFO(NULL, "Send value 2 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 2.0);
if (rv<0) {
DBG_INFO(NULL, "Error sending data: %d", rv);
}
}
else {
DBG_INFO(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName);
}
}
}
void _sendPageList(AQH_MODULE *m, GWEN_BUFFER *dbuf)
{
GWEN_STRINGLIST *sl;
GBAS(dbuf, "<h1>Page List</h1>\n");
sl=_listPageFiles(m);
if (sl) {
GWEN_STRINGLISTENTRY *se;
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead><tr><th>Page</th><th>Title</th></tr></thead>\n"
"<tbody>\n");
se=GWEN_StringList_FirstEntry(sl);
while(se) {
const char *filename;
filename=GWEN_StringListEntry_Data(se);
if (filename && *filename) {
GWEN_XMLNODE *node;
DBG_ERROR(NULL, "Reading file \"%s\"", filename);
node=_readPageFile(filename);
if (node) {
GWEN_XMLNODE *nPage;
nPage=GWEN_XMLNode_FindFirstTag(node, "page", NULL, NULL);
if (nPage) {
const char *sId;
const char *sTitle;
sId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
sTitle=GWEN_XMLNode_GetProperty(nPage, "title", sId);
if (sId && *sId)
GBAA(dbuf, "<tr><td><a href=\"page.html?page=%s\">%s</a></td><td>%s</td></tr>\n", sId, sId, sTitle);
}
GWEN_XMLNode_free(node);
}
}
se=GWEN_StringListEntry_Next(se);
}
GBAS(dbuf, "</tbody></table>");
GWEN_StringList_free(sl);
}
else {
GBAS(dbuf, "No pages.");
}
}
GWEN_STRINGLIST *_listPageFiles(AQH_MODULE *m)
{
GWEN_BUFFER *fbuf;
AQH_SERVICE *sv;
GWEN_STRINGLIST *sl;
int rv;
sv=AQH_ModService_GetService(m);
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAA(fbuf, "%s%spages", AQH_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S);
sl=GWEN_StringList_new();
rv=GWEN_Directory_GetMatchingFilesRecursively(GWEN_Buffer_GetStart(fbuf), sl, "*.xml");
if (rv<0) {
DBG_INFO(NULL, "Error reading pages (%d)", rv);
GWEN_StringList_free(sl);
GWEN_Buffer_free(fbuf);
return NULL;
}
if (GWEN_StringList_Count(sl)<1) {
GWEN_StringList_free(sl);
GWEN_Buffer_free(fbuf);
return NULL;
}
GWEN_Buffer_free(fbuf);
return sl;
}
GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName)
{
GWEN_BUFFER *fbuf;
AQH_SERVICE *sv;
GWEN_XMLNODE *fileNode;
sv=AQH_ModService_GetService(m);
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAA(fbuf, "%s%spages%s", AQH_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S, GWEN_DIR_SEPARATOR_S);
AQH_ModService_EscapeToBuffer(sPageName, fbuf);
GBAS(fbuf, ".xml");
fileNode=_readPageFile(GWEN_Buffer_GetStart(fbuf));
if (fileNode==NULL) {
DBG_INFO(NULL, "here");
GWEN_Buffer_free(fbuf);
return NULL;
}
GWEN_Buffer_free(fbuf);
return fileNode;
}
GWEN_XMLNODE *_readPageFile(const char *sFilename)
{
GWEN_XMLNODE *fileNode;
int rv;
fileNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, sFilename);
rv=GWEN_XML_ReadFile(fileNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
if (rv<0) {
DBG_ERROR(NULL, "Error reading \"%s\": %s (%d)", sFilename?sFilename:"<no name>", strerror(errno), errno);
GWEN_XMLNode_free(fileNode);
return NULL;
}
return fileNode;
}
int _layoutFromString(const char *s)
{
if (s && *s) {
if (strcasecmp(s, "none")==0)
return MY_LAYOUT_NONE;
else if (strcasecmp(s, "horizontal")==0)
return MY_LAYOUT_HORIZONTAL;
else if (strcasecmp(s, "vertical")==0)
return MY_LAYOUT_VERTICAL;
}
return MY_LAYOUT_NONE;
}

View File

@@ -0,0 +1,29 @@
/****************************************************************************
* 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_CGI_MDEVICES_PAGE_H
#define AQHOME_CGI_MDEVICES_PAGE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,194 @@
/****************************************************************************
* 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 "./mdevices_setdata.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
static void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
static void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sDeviceName;
const char *sValueName;
const AQH_VALUE *value;
/* sample data */
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sDeviceName=dbPost?GWEN_DB_GetCharValue(dbPost, "device", 0, NULL):NULL;
sValueName=dbPost?GWEN_DB_GetCharValue(dbPost, "value", 0, NULL):NULL;
DBG_ERROR(NULL, "Device=[%s], value=[%s]", sDeviceName?sDeviceName:"", sValueName?sValueName:"");
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
if(value && AQH_Value_GetValueType(value)==AQH_ValueType_Actor) {
const char *sValueName;
const char *sValue;
sValueName=AQH_Value_GetName(value);
sValue=GWEN_DB_GetCharValue(dbPost, sValueName, 0, NULL);
if (sValueName && *sValueName) {
DBG_ERROR(NULL, "Setting value %s to %s", sValueName?sValueName:"no name", sValue?sValue:"no value");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW: _setRgbwData(dc, value, sValue); break;
case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sValue); break;
case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sValue); break;
default:
break;
} /* switch */
} /* if (sValueName) */
}
if (sDeviceName && *sDeviceName) {
GWEN_BUFFER *pbuf;
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
if (sValueName && *sValueName) {
GBAS(pbuf, "Location: value.html?device=");
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
GBAS(pbuf, "&value=");
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
}
else {
GBAS(pbuf, "Location: values.html?device=");
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
}
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
GWEN_Buffer_free(pbuf);
}
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
void _setRgbwData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
uint32_t color;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
color=AQH_ModDevices_ColorFromHexString(sValue);
DBG_ERROR(NULL, "Send value [#%08x] to %s", color, sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, (double) color);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
}
void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
if (strcasecmp(sValue, "unchanged")==0) {
DBG_ERROR(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else {
}
}
}
void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
{
if (sValue) {
const char *sValueSystemName;
int rv;
sValueSystemName=AQH_Value_GetNameForSystem(value);
if (strcasecmp(sValue, "unchanged")==0) {
DBG_ERROR(NULL, "Value %s unchanged", sValueSystemName);
}
else if (strcasecmp(sValue, "on")==0) {
DBG_ERROR(NULL, "Send value 1 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 1.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "off")==0) {
DBG_ERROR(NULL, "Send value 0 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 0.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else if (strcasecmp(sValue, "auto")==0) {
DBG_ERROR(NULL, "Send value 2 to %s", sValueSystemName);
rv=AQH_DataClient_SetData(dc, value, 2.0);
if (rv<0) {
DBG_ERROR(NULL, "Error sending data: %d", rv);
}
}
else {
DBG_ERROR(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName);
}
}
}

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_CGI_MDEVICES_SETVALUE_H
#define AQHOME_CGI_MDEVICES_SETVALUE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,105 @@
/****************************************************************************
* 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 "./mdevices_setdevice.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _handleDeviceForm(AQH_DATACLIENT *dc, AQH_DEVICE *device, GWEN_DB_NODE *dbPost, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunSetDevice(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
GWEN_DB_NODE *dbPost;
const char *sDeviceName;
AQH_DEVICE *device;
DBG_ERROR(NULL, "Post device.html");
/* sample data */
sv=AQH_ModService_GetService(m);
dbPost=AQCGI_Request_GetDbPostBody(rq);
sDeviceName=dbPost?GWEN_DB_GetCharValue(dbPost, "device", 0, NULL):NULL;
DBG_ERROR(NULL, "Device=[%s]", sDeviceName?sDeviceName:"");
device=AQH_ModDevices_GetDevice(dc, sDeviceName);
if (device) {
int rv;
DBG_ERROR(NULL, "Reading data from form");
_handleDeviceForm(dc, device, dbPost, dbuf);
DBG_ERROR(NULL, "Updating device on server");
rv=AQH_DataClient_ModDevice(dc, device);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
}
AQH_Device_free(device);
}
else {
DBG_ERROR(NULL, "device not found");
}
if (sDeviceName && *sDeviceName) {
GWEN_BUFFER *pbuf;
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAS(pbuf, "Location: device.html?device=");
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
GWEN_Buffer_free(pbuf);
}
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
}
void _handleDeviceForm(AQH_DATACLIENT *dc, AQH_DEVICE *device, GWEN_DB_NODE *dbPost, GWEN_BUFFER *dbuf)
{
AQH_Device_SetRoomName(device, GWEN_DB_GetCharValue(dbPost, "roomName", 0, NULL));
AQH_Device_SetNameForGui(device, GWEN_DB_GetCharValue(dbPost, "nameForGui", 0, NULL));
AQH_Device_SetLocation(device, GWEN_DB_GetCharValue(dbPost, "location", 0, NULL));
AQH_Device_SetDescription(device, GWEN_DB_GetCharValue(dbPost, "description", 0, NULL));
}

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_CGI_MDEVICES_SETDEVICE_H
#define AQHOME_CGI_MDEVICES_SETDEVICE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunSetDevice(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,245 @@
/****************************************************************************
* 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 "./mdevices_value.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runValueWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_DATACLIENT *dc,
const char *sDeviceName,
const char *sValueName,
GWEN_BUFFER *dbuf);
static void _mkValueForm(AQH_DATACLIENT *dc, const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf);
static void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
static void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunValue(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
const char *sValueName;
DBG_ERROR(NULL, "RunValue");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
sValueName=GWEN_DB_GetCharValue(dbQuery, "value", 0, NULL);
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
GWEN_BUFFER *bufDeviceName;
GWEN_BUFFER *bufValueName;
bufDeviceName=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Text_UnescapeToBufferTolerant(sDeviceName, bufDeviceName);
bufValueName=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Text_UnescapeToBufferTolerant(sValueName, bufValueName);
_runValueWithArgs(m, rq, session, dc,
GWEN_Buffer_GetStart(bufDeviceName),
GWEN_Buffer_GetStart(bufValueName),
dbuf);
GWEN_Buffer_free(bufValueName);
GWEN_Buffer_free(bufDeviceName);
}
#if 0
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
GWEN_BUFFER *pbuf;
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAS(pbuf, "Location: /aqbt/devices/value.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, pbuf);
GBAS(pbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, pbuf);
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
GWEN_Buffer_free(pbuf);
}
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
#endif
}
void _runValueWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_DATACLIENT *dc,
const char *sDeviceName,
const char *sValueName,
GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
AQH_VALUE_LIST *valueList;
dbQuery=AQCGI_Request_GetDbQuery(rq);
DBG_ERROR(NULL, "Device=%s, value=%s", sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>");
GBAA(dbuf,"<h1>Value %s/%s</h1>\n", sDeviceName, sValueName);
valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0);
if (valueList) {
const AQH_VALUE *value;
value=AQH_Value_List_First(valueList);
while(value) {
const char *s;
s=AQH_Value_GetName(value);
if (s && *s && strcasecmp(s, sValueName)==0)
break;
value=AQH_Value_List_Next(value);
}
if (value && AQH_Value_GetValueType(value)==AQH_ValueType_Actor) {
_mkValueForm(dc, sDeviceName, value, dbuf);
}
else {
GBAS(dbuf, "<table>\n");
GBAS(dbuf, "<tr><td>");
GBAS(dbuf, "<img src=\"graph.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, dbuf);
GBAS(dbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, dbuf);
GBAS(dbuf, "&period=4h\"/>");
GBAS(dbuf, "</td></tr>");
GBAS(dbuf, "<tr><td>");
GBAS(dbuf, "<img src=\"graph.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, dbuf);
GBAS(dbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, dbuf);
GBAS(dbuf, "&period=1d\"/>");
GBAS(dbuf, "</td></tr>");
GBAS(dbuf, "<tr><td>");
GBAS(dbuf, "<img src=\"graph.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, dbuf);
GBAS(dbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, dbuf);
GBAS(dbuf, "&period=1w\"/>");
GBAS(dbuf, "</td></tr>");
GBAS(dbuf, "</table>");
}
AQH_Value_List_free(valueList);
}
}
void _mkValueForm(AQH_DATACLIENT *dc, const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueSystemName;
const char *sValueName;
uint64_t dataPoints[2];
uint64_t recvdNum;
// uint64_t timestamp;
union {double f; uint64_t i;} u;
int intVal;
sValueSystemName=AQH_Value_GetNameForSystem(value);
sValueName=AQH_Value_GetName(value);
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
if (recvdNum>0) {
// timestamp=dataPoints[0];
u.i=dataPoints[1];
intVal=(int) u.f;
}
else {
u.i=0;
intVal=-1;
}
GBAS(dbuf,"<form action=\"setdata.html\" method=\"post\">\n");
GBAA(dbuf, "<input type=\"hidden\" name=\"device\" value=\"%s\">\n", sDeviceName);
GBAA(dbuf, "<input type=\"hidden\" name=\"value\" value=\"%s\">\n", sValueName);
DBG_ERROR(NULL, "Adding actor");
switch(AQH_Value_GetModality(value)) {
case AQH_ValueModality_RGBW: _writeRgbwToForm(sValueName, u.f, dbuf); break;
case AQH_ValueModality_OnOff: _writeOnOffToForm(sValueName, intVal, dbuf); break;
case AQH_ValueModality_OnOffAuto: _writeOnOffAutoToForm(sValueName, intVal, dbuf); break;
default: GBAA(dbuf, "%.2f", u.f); break;
} /* switch */
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
GBAS(dbuf, "</form>\n\n");
}
void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf)
{
#if 1
GBAA(dbuf, "<input type=\"text\" name=\"%s\" value=\"#%08x\"/>", sValueName, color);
#else
GBAA(dbuf, "<input type=\"color\" name=\"%s\" value=\"#%08x\"/>#%08x (#%08x)",
sValueName,
AQH_ModDevices_RgbwToHtmlColor(color),
AQH_ModDevices_RgbwToHtmlColor(color),
color);
#endif
}
void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAS(dbuf, "</select>");
}
void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
{
GBAA(dbuf, "<select name=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName);
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
GBAS(dbuf, "</select>");
}

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_CGI_MDEVICES_VALUE_H
#define AQHOME_CGI_MDEVICES_VALUE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunValue(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,175 @@
/****************************************************************************
* 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 "./mdevices_valuesgraph.h"
#include "./mdevices_index.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _writeValueToDetailedTable(const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static void _writeValueListToTable(const char *sDeviceName, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf);
static void _writeValueToTable(const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
static void _addGraphLink(const char *sDeviceName, const char *sValueName, const char *sPeriod, GWEN_BUFFER *dbuf, int withLink);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunValuesAsGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
if (!(sDeviceName && *sDeviceName))
AQH_ModDevices_RunIndex(m, rq, session, dc, dbuf);
else {
const char *sValueName;
sValueName=GWEN_DB_GetCharValue(dbQuery, "value", 0, NULL);
if (sValueName && *sValueName) {
AQH_VALUE *value;
GBAA(dbuf,"<h1>Value %s/%s</h1>\n", sDeviceName, sValueName);
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
if (value) {
_writeValueToDetailedTable(sDeviceName, value, dbuf);
AQH_Value_free(value);
AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120");
}
}
else {
AQH_VALUE_LIST *valueList;
valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0);
if (valueList && AQH_Value_List_GetCount(valueList)) {
GBAA(dbuf,"<h1>Values for Device %s</h1>\n", sDeviceName);
_writeValueListToTable(sDeviceName, valueList, dbuf);
GBAS(dbuf, "\n");
}
else {
GBAS(dbuf,"<p>No values.</p>\n");
}
AQH_Value_List_free(valueList);
AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 305");
}
}
}
void _writeValueToDetailedTable(const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueName;
GBAS(dbuf, "<table>\n");
sValueName=AQH_Value_GetName(value);
GBAS(dbuf, "<tr><td>");
_addGraphLink(sDeviceName, sValueName, "4h", dbuf, 0);
GBAS(dbuf, "</td><td>");
_addGraphLink(sDeviceName, sValueName, "1d", dbuf, 0);
GBAS(dbuf, "</td></tr><tr><td>");
_addGraphLink(sDeviceName, sValueName, "1w", dbuf, 0);
GBAS(dbuf, "</td><td>");
_addGraphLink(sDeviceName, sValueName, "12m", dbuf, 0);
GBAS(dbuf, "</td></tr>\n");
}
void _writeValueListToTable(const char *sDeviceName, const AQH_VALUE_LIST *valueList, GWEN_BUFFER *dbuf)
{
const AQH_VALUE *value;
GBAS(dbuf, "<table>\n");
value=AQH_Value_List_First(valueList);
while(value) {
if (AQH_Value_GetValueType(value)!=AQH_ValueType_Actor)
_writeValueToTable(sDeviceName, value, dbuf);
value=AQH_Value_List_Next(value);
}
GBAS(dbuf, "</table>\n");
}
void _writeValueToTable(const char *sDeviceName, const AQH_VALUE *value, GWEN_BUFFER *dbuf)
{
const char *sValueName;
/* name */
sValueName=AQH_Value_GetName(value);
GBAS(dbuf, "<tr><td>");
_addGraphLink(sDeviceName, sValueName, "1d", dbuf, 1);
GBAS(dbuf, "</td><td>");
_addGraphLink(sDeviceName, sValueName, "1w", dbuf, 1);
GBAS(dbuf, "</td></tr>\n");
}
void _addGraphLink(const char *sDeviceName, const char *sValueName, const char *sPeriod, GWEN_BUFFER *dbuf, int withLink)
{
if (withLink) {
GBAS(dbuf, "<a href=\"vgraph.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, dbuf);
GBAS(dbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, dbuf);
GBAS(dbuf, "\">");
}
GBAS(dbuf, "<img src=\"graph.html?device=");
GWEN_Text_EscapeToBufferTolerant(sDeviceName, dbuf);
GBAS(dbuf, "&value=");
GWEN_Text_EscapeToBufferTolerant(sValueName, dbuf);
GBAA(dbuf, "&period=%s\"", sPeriod);
GBAA(dbuf, " alt=\"%s\" width=\"%d\" height=\"%d\"", sValueName, AQH_MODDEVICES_GRAPH_WIDTH, AQH_MODDEVICES_GRAPH_HEIGHT);
GBAS(dbuf, "/>");
if (withLink) {
GBAS(dbuf, "</a>");
}
}

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_CGI_MDEVICES_VALUESGRAPH_H
#define AQHOME_CGI_MDEVICES_VALUESGRAPH_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunValuesAsGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,145 @@
/****************************************************************************
* 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 "./mdevices_valuestable.h"
#include "./mdevices_index.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _writeValueListToTable(const char *sDeviceName, const AQH_VALUE_LIST *valueList, uint32_t perms, GWEN_BUFFER *dbuf);
static void _writeValueToTable(const char *sDeviceName, const AQH_VALUE *value, uint32_t perms, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunValuesAsTable(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
uint32_t perms;
perms=AQH_ModService_GetUserPerms(m);
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
if (!(sDeviceName && *sDeviceName))
AQH_ModDevices_RunIndex(m, rq, session, dc, dbuf);
else {
AQH_VALUE_LIST *valueList;
valueList=AQH_DataClient_GetValues(dc, sDeviceName, 0);
if (valueList && AQH_Value_List_GetCount(valueList)) {
GBAA(dbuf,"<h1>Values for Device %s</h1>\n", sDeviceName);
_writeValueListToTable(sDeviceName, valueList, perms, dbuf);
GBAS(dbuf, "\n");
}
else {
GBAS(dbuf,"<p>No values.</p>\n");
}
AQH_Value_List_free(valueList);
}
}
void _writeValueListToTable(const char *sDeviceName, const AQH_VALUE_LIST *valueList, uint32_t perms, GWEN_BUFFER *dbuf)
{
const AQH_VALUE *value;
GBAS(dbuf,
"<table class=\"datatable\">\n"
"<thead>"
"<tr>"
"<th>Name</th>"
"<th>Type</th>"
"<th>Modality</th>"
#if 0
"<th>Driver</th>"
"<th>Device</th>"
"<th>Name for System</th>"
#endif
"</tr>"
"</thead>\n"
"<tbody>\n");
value=AQH_Value_List_First(valueList);
while(value) {
//if (AQH_Value_GetModality(value)!=AQH_ValueModality_Stats)
_writeValueToTable(sDeviceName, value, perms, dbuf);
value=AQH_Value_List_Next(value);
}
GBAS(dbuf,
"</tbody>\n"
"</table>\n");
}
void _writeValueToTable(const char *sDeviceName, const AQH_VALUE *value, uint32_t perms, GWEN_BUFFER *dbuf)
{
const char *s;
GBAS(dbuf, "<tr>");
/* name for system */
s=AQH_Value_GetName(value);
if (perms & AQH_MODDEVICES_PERMS_VALUEREAD) {
uint32_t pos;
pos=GWEN_Buffer_GetPos(dbuf);
GBAS(dbuf,"<td><a href=\"value.html?device=");
GWEN_Text_EscapeToBuffer(sDeviceName, dbuf);
GBAS(dbuf,"&value=");
GWEN_Text_EscapeToBuffer(s, dbuf);
GBAA(dbuf,"\">%s</a></td>", s);
}
else
GBAA(dbuf,"<td>%s</td>", s?s:"");
s=AQH_ValueType_toString(AQH_Value_GetValueType(value));
GBAA(dbuf, "<td>%s</td>", s?s:"");
s=AQH_ValueModality_toString(AQH_Value_GetModality(value));
GBAA(dbuf, "<td>%s</td>", s?s:"");
GBAA(dbuf, "</tr>\n");
}

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_CGI_MDEVICES_VALUESTABLE_H
#define AQHOME_CGI_MDEVICES_VALUESTABLE_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunValuesAsTable(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,279 @@
/****************************************************************************
* 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 "./mdevices_vgraph.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/mdataclient.h"
#include <aqdiagram/graph/timegraph.h>
#include <aqdiagram/graph/w_graph.h>
#include <aqdiagram/draw/context_cairo.h>
//#include <aqdiagram/data/date.h>
//#include <aqdiagram/data/floatingavg.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
/* ------------------------------------------------------------------------------------------------
* vars
* ------------------------------------------------------------------------------------------------
*/
typedef struct MY_GRAPH_PARAMS MY_GRAPH_PARAMS;
struct MY_GRAPH_PARAMS {
const char *name;
const char *title;
const char *modifiers;
int startTimeDiff;
int acceptedAgeInSeconds;
};
static MY_GRAPH_PARAMS _graphParams[]={
{"4h", "last 4 hours", "Lm5", 4*60*60, 2*60},
{"1d", "last 24 hours", "Lm30", 24*60*60, 5*60},
{"1w", "last 7 days", "Lm240", 7*24*60*60, 15*60},
{"1m", "last 30 days", "Lm480", 30*24*60*60, 60*60},
{"6m", "last 6 months", "Lm720", 182*24*60*60, 60*60},
{"12m","last 12 months", "Lm1440", 365*24*60*60, 60*60},
{NULL, NULL, NULL, 0, 0}
};
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _runGraphValueWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_DATACLIENT *dc,
const char *sDeviceName,
const char *sValueName,
GWEN_BUFFER *dbuf);
static void _createGraph(AQH_DATACLIENT *dc,
const AQH_VALUE *v,
const MY_GRAPH_PARAMS *graphParams,
const char *graphTitle, int precision, const char *curveLabel,
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
static AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision);
static void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf);
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDevices_RunGraphValue(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
const char *sDeviceName;
const char *sValueName;
DBG_DEBUG(NULL, "GraphValue");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
sValueName=GWEN_DB_GetCharValue(dbQuery, "value", 0, NULL);
DBG_DEBUG(NULL, "Device=%s, value=%s", sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>");
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
GWEN_BUFFER *bufDeviceName;
GWEN_BUFFER *bufValueName;
bufDeviceName=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Text_UnescapeToBufferTolerant(sDeviceName, bufDeviceName);
bufValueName=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Text_UnescapeToBufferTolerant(sValueName, bufValueName);
_runGraphValueWithArgs(m, rq, dc,
GWEN_Buffer_GetStart(bufDeviceName),
GWEN_Buffer_GetStart(bufValueName),
dbuf);
GWEN_Buffer_free(bufValueName);
GWEN_Buffer_free(bufDeviceName);
}
}
void _runGraphValueWithArgs(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_DATACLIENT *dc,
const char *sDeviceName,
const char *sValueName,
GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbQuery;
AQH_VALUE *value;
const MY_GRAPH_PARAMS *graphParams;
const char *sPeriod;
DBG_DEBUG(NULL, "GraphValue with args");
dbQuery=AQCGI_Request_GetDbQuery(rq);
sPeriod=GWEN_DB_GetCharValue(dbQuery, "period", 0, NULL);
graphParams=_getParamsByName(sPeriod);
if (graphParams==NULL)
graphParams=&_graphParams[0];
DBG_DEBUG(NULL, "Device=%s, value=%s, period=%s",
sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>",
sPeriod?sPeriod:"<empty>");
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
if (value) {
GWEN_BUFFER *fbuf;
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
DBG_DEBUG(NULL, "Creating graph");
_createGraph(dc,
value,
graphParams,
sValueName,
2,
AQH_ValueModality_toString(AQH_Value_GetModality(value)),
GWEN_Buffer_GetStart(fbuf),
AQH_MODDEVICES_GRAPH_WIDTH, AQH_MODDEVICES_GRAPH_HEIGHT,
100000);
}
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
GWEN_Buffer_free(fbuf);
AQH_Value_free(value);
}
else {
DBG_ERROR(NULL, "Could not get value \"%s/%s\"", sDeviceName, sValueName);
}
}
void _createGraph(AQH_DATACLIENT *dc,
const AQH_VALUE *v,
const MY_GRAPH_PARAMS *graphParams,
const char *graphTitle, int precision,
const char *curveLabel,
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints)
{
const char *sValue;
AQDG_GRAPH *g;
AQDG_DRAW_CONTEXT *drawContext;
AQDG_OBJECT *graphObject;
uint64_t tsBegin;
uint64_t tsEnd;
AQDG_GRAPH_DATAPAIR_LIST *dpList;
sValue=AQH_Value_GetNameForSystem(v);
tsEnd=time(0);
tsBegin=time(0)-(graphParams->startTimeDiff);
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
dpList=AQH_ModDevices_RequestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
if (dpList) {
DBG_DEBUG(NULL, "Adding data for %s", sValue);
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
}
else {
DBG_ERROR(NULL, "No data for %s", sValue);
AQDG_Graph_free(g);
return;
}
AQDG_TimeGraph_SetupTicks(g, 0, 0.0, 0.0);
DBG_DEBUG(NULL, "Draw graph for %s", sValue);
drawContext=AQDG_Draw_ContextCairo_Png_new(sImgFile, imgWidth, imgHeight);
graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
AQDG_Object_SetWidth(graphObject, imgWidth);
AQDG_Object_SetHeight(graphObject, imgHeight);
AQDG_GraphWidget_SetupDefaultPens(graphObject);
AQDG_GraphWidget_SetupDefaultFonts(graphObject);
AQDG_GraphWidget_FinishWithGraph(graphObject, g);
AQDG_Object_free(graphObject);
}
AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision)
{
AQDG_GRAPH *g;
GWEN_BUFFER *tbuf;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GBAA(tbuf, "%s - %s", graphTitle, graphParams->title);
g=AQDG_TimeGraph_new(GWEN_Buffer_GetStart(tbuf), NULL, "Value", NULL, precision);
GWEN_Buffer_free(tbuf);
return g;
}
void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
const char *s;
sv=AQH_ModService_GetService(m);
/* cache folder */
s=AQH_Service_GetCacheFolder(sv);
GBAA(dbuf, "%s%s", s, GWEN_DIR_SEPARATOR_S);
/* var name */
s=AQH_Value_GetNameForSystem(v);
AQH_ModService_EscapeToBuffer(s, dbuf);
GBAA(dbuf, "-%s.png", graphParams->name);
}
const MY_GRAPH_PARAMS *_getParamsByName(const char *s)
{
const MY_GRAPH_PARAMS *p;
p=_graphParams;
while(p->name) {
if (strcasecmp(p->name, s)==0)
return p;
p++;
}
return NULL;
}

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_CGI_MDEVICES_VGRAPH_H
#define AQHOME_CGI_MDEVICES_VGRAPH_H
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
void AQH_ModDevices_RunGraphValue(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

View File

@@ -0,0 +1,63 @@
body {
background-color: whitesmoke;
}
table.datatable {
border: thin solid;
border-collapse: collapse;
}
table.datatable th, td {
border: thin solid;
border-collapse: collapse;
padding: 5px;
}
table.datatable tbody tr:nth-child(odd) {
background-color: #ffffff;
}
table.formtable {
border: thin solid;
border-collapse: collapse;
}
table.formtable th, td {
border: thin solid;
border-collapse: collapse;
padding: 5px;
}
ul.mainmenu {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333333;
}
ul.mainmenu li {
float: left;
}
ul.mainmenu li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
ul.mainmenu li a:hover {
background-color: #111111;
}

View File

@@ -0,0 +1,102 @@
/****************************************************************************
* 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 "./mdataclient.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
void AQH_ModDataClient_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder)
{
AQH_ModService_Extend(m, sv, baseFolder);
}
void AQH_ModDataClient_HandleRequest(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_MODDATACLIENT_RUN_FN runFn,
GWEN_BUFFER *dbuf)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
rv=AQH_Init();
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
return;
}
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadConfigFile(dc);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
return;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
return;
}
if (runFn)
runFn(m, rq, session, dc, dbuf);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
AQH_Fini();
}

View File

@@ -0,0 +1,37 @@
/****************************************************************************
* 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_CGI_MDATACLIENT_H
#define AQHOME_CGI_MDATACLIENT_H
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/buffer.h>
typedef void (*AQH_MODDATACLIENT_RUN_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
void AQH_ModDataClient_Extend(AQH_MODULE *m, AQH_SERVICE *sv, const char *baseFolder);
void AQH_ModDataClient_HandleRequest(AQH_MODULE *m,
AQCGI_REQUEST *rq,
AQH_SESSION *session,
AQH_MODDATACLIENT_RUN_FN runFn,
GWEN_BUFFER *dbuf);
#endif

View File

@@ -0,0 +1,326 @@
/****************************************************************************
* 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 "./mroot_p.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/modules/devices/mdevices.h"
#include "aqhome-cgi/modules/common/madmin.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem);
static int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
static int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
static int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
static AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MODULE *m;
m=AQH_Module_new();
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
return m;
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sModuleName)
{
AQH_SERVICE *sv;
sv=AQH_ModService_GetService(m);
if (strcasecmp(sModuleName, "devices")==0) {
AQH_MODULE *mSub;
mSub=AQH_Service_LoadModule(sv, sModuleName);
if (mSub) {
const char *s;
GWEN_BUFFER *nbuf;
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
s=AQH_ModService_GetBaseFolder(m);
GWEN_Buffer_AppendArgs(nbuf, "%s/devices", s?s:".");
AQH_ModDevices_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
AQH_Module_Tree2_AddChild(m, mSub);
GWEN_Buffer_free(nbuf);
return mSub;
}
}
else if (strcasecmp(sModuleName, "admin")==0) {
AQH_MODULE *mSub;
mSub=AQH_Service_LoadModule(sv, sModuleName);
if (mSub) {
const char *s;
GWEN_BUFFER *nbuf;
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
s=AQH_ModService_GetBaseFolder(m);
GWEN_Buffer_AppendArgs(nbuf, "%s/devices", s?s:".");
AQH_ModAdmin_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
AQH_Module_Tree2_AddChild(m, mSub);
GWEN_Buffer_free(nbuf);
return mSub;
}
}
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, const char *sLastPathElem)
{
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
AQH_ModService_AddHeader(m, "en", dbuf);
if (strcasecmp(sLastPathElem, "login")==0)
_handleRqLogin(m, rq, dbuf);
else if (strcasecmp(sLastPathElem, "signup")==0) {
AQCGI_Request_SetResponseCode(rq, 501);
AQCGI_Request_SetResponseText(rq, "Not Implemented");
}
else if (strcasecmp(sLastPathElem, "confirm")==0) {
AQCGI_Request_SetResponseCode(rq, 501);
AQCGI_Request_SetResponseText(rq, "Not Implemented");
}
else if (strcasecmp(sLastPathElem, "index.html")==0)
_handleRqIndex(m, rq, dbuf);
else {
AQCGI_Request_SetResponseCode(rq, 404);
AQCGI_Request_SetResponseText(rq, "Not Found");
}
AQH_ModService_AddFooter(m, "en", dbuf);
AQCGI_Request_SetBufferResponseBody(rq, dbuf);
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
return AQCGI_SendResponse(rq);
}
int _handleRqIndex(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
return AQH_ModService_RespondWithFile(m, rq, "en", "index.html", dbuf);
else {
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
AQCGI_Request_SetResponseCode(rq, 405);
AQCGI_Request_SetResponseText(rq, "Method Not Allowed");
return GWEN_ERROR_GENERIC;
}
}
int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
AQH_ModService_RespondWithFile(m, rq, "en", "login.html", dbuf);
else if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_POST)
_handleRqLoginPost(m, rq, dbuf);
else {
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
AQCGI_Request_SetResponseCode(rq, 405);
AQCGI_Request_SetResponseText(rq, "Method Not Allowed");
return GWEN_ERROR_GENERIC;
}
return 0;
}
int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
AQH_USER *user;
AQH_SESSION *session;
GWEN_BUFFER *tbuf;
GWEN_TIMESTAMP *ts;
int rv;
DBG_ERROR(NULL, "Handling request");
sv=AQH_ModService_GetService(m);
user=_getAndCheckUser(m, rq, dbuf);
if (user==NULL) {
DBG_INFO(NULL, "here");
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
return GWEN_ERROR_GENERIC;
}
ts=GWEN_Timestamp_NowInLocalTime();
AQH_User_SetTimestampLastLogin(user, ts);
DBG_ERROR(NULL, "Saving user");
rv=AQH_Service_SaveUser(sv, user);
if (rv<0) {
DBG_ERROR(NULL, "Error saving user \"%s\"", AQH_User_GetAlias(user));
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
AQH_User_free(user);
return rv;
}
/* generate session */
DBG_ERROR(NULL, "Generating session");
tbuf=GWEN_Buffer_new(0, 64, 0, 1);
AQCGI_GenerateSessionId(tbuf);
session=AQH_Session_new();
AQH_Session_SetTimestampCreation(session, ts);
AQH_Session_SetTimestampLastAccess(session, ts);
AQH_Session_SetUid(session, GWEN_Buffer_GetStart(tbuf));
GWEN_Buffer_free(tbuf);
AQH_Session_SetUserAlias(session, AQH_User_GetAlias(user));
rv=AQH_Service_AddSession(sv, session);
if (rv<0) {
DBG_ERROR(NULL, "Error adding session for user \"%s\" (%d)", AQH_User_GetAlias(user), rv);
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
AQH_Session_free(session);
AQH_User_free(user);
return GWEN_ERROR_INTERNAL;
}
/* add Set-Cookie header */
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(tbuf, "Set-Cookie: session=%s; max-age=86400", AQH_Session_GetUid(session));
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
GWEN_Buffer_free(tbuf);
/* finish */
AQCGI_Request_AddResponseHeaderData(rq, "Location: index.html");
AQCGI_Request_SetResponseCode(rq, 303);
AQCGI_Request_SetResponseText(rq, "See other");
AQH_Session_free(session);
AQH_User_free(user);
return 0;
}
AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbPost;
dbPost=AQCGI_Request_GetDbPostBody(rq);
if (dbPost) {
AQH_SERVICE *sv;
const char *sUserName;
const char *sPasswd;
AQH_USER *user;
const char *hashedPaswd;
GWEN_BUFFER *buf;
sv=AQH_ModService_GetService(m);
sUserName=GWEN_DB_GetCharValue(dbPost, "userid", 0, NULL);
sPasswd=GWEN_DB_GetCharValue(dbPost, "password", 0, NULL);
if (!(sUserName && *sUserName && sPasswd && *sPasswd)) {
DBG_ERROR(NULL, "Either user name or password missing");
AQCGI_Request_SetResponseCode(rq, 400);
AQCGI_Request_SetResponseText(rq, "Bad Request");
return NULL;
}
DBG_ERROR(NULL, "Loading user \"%s\" (%p)", sUserName, sv);
user=AQH_Service_LoadUser(sv, sUserName);
if (user==NULL) {
DBG_ERROR(NULL, "User \"%s\" not found", sUserName);
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
return NULL;
}
DBG_ERROR(NULL, "Loaded user \"%s\"", sUserName);
if (AQH_User_GetState(user)!=AQH_UserState_Active) {
DBG_ERROR(NULL, "User \"%s\" not active", sUserName);
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
AQH_User_free(user);
return NULL;
}
hashedPaswd=AQH_User_GetHashedPassword(user);
if (!(hashedPaswd && *hashedPaswd)) {
DBG_ERROR(NULL, "User \"%s\" has no hashed password", sUserName);
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
AQH_User_free(user);
return NULL;
}
buf=GWEN_Buffer_new(0, 256, 0, 1);
AQCGI_HashMd256ToBuffer(sPasswd, buf);
DBG_ERROR(NULL, "Password: [%s]", sPasswd); // TODO: Remove!!!
DBG_ERROR(NULL, "Hashed password: [%s]", GWEN_Buffer_GetStart(buf));
if (strcasecmp(GWEN_Buffer_GetStart(buf), hashedPaswd)!=0) {
DBG_ERROR(NULL, "Bad password for user \"%s\"", sUserName);
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
GWEN_Buffer_free(buf);
AQH_User_free(user);
return NULL;
}
GWEN_Buffer_free(buf);
DBG_ERROR(NULL, "User \"%s\" accepted", sUserName);
return user;
}
else {
DBG_ERROR(NULL, "No POST data");
AQCGI_SendResponseWithStatus(rq, 400, "Bad Request");
AQCGI_Request_SetResponseCode(rq, 400);
AQCGI_Request_SetResponseText(rq, "Bad Request");
return NULL;
}
}

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_CGI_MROOT_H
#define AQHOME_CGI_MROOT_H
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder);
AQH_MODULE *AQH_ModRoot_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
int AQH_ModRoot_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
#endif

View File

@@ -0,0 +1,18 @@
/****************************************************************************
* 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_CGI_MROOT_P_H
#define AQHOME_CGI_MROOT_P_H
#include "aqhome-cgi/modules/mroot.h"
#endif

View File

@@ -0,0 +1,7 @@
<?xml?>
<gwbuild>
<subdirs>
en
</subdirs>
</gwbuild>

View File

@@ -0,0 +1,14 @@
<?xml?>
<gwbuild>
<data dist="true" install="$(httpdatadir)/aqhome-cgi/static/en">
header.html
footer.html
login.html
index.html
</data>
</gwbuild>

View File

@@ -0,0 +1,4 @@
</body>
</html>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- copyright (c) 2025 by martin@libchipcard.de -->
<meta name="generator" content="FTE 1.1" />
<meta name="revised" content="martin,2025-06-12" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="author" content="martin" />
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<link rel="stylesheet" href="/style.css" >
<title>AqHome</title>
</head>
<ul class="mainmenu" >
<li><a href="/aqhome/devices/index.html">Devices</a></li>
<li><a href="/aqhome/devices/page.html">Pages</a></li>
<li><a href="/aqhome/admin/index.html">Admin</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li style="float:right"><a href="/aqhome/login">Login</a></li>
</ul>
<body>

View File

@@ -0,0 +1,6 @@
<h1> AqHome Main Page </h1>
<p>Nothing to see for now</p>

View File

@@ -0,0 +1,25 @@
<div class="main">
<h1>AqHome</h1>
<h3>Enter your login credentials</h3>
<form action="login" method="post">
<label for="userid">Username:</label>
<input type="text" id="userid" name="userid" placeholder="Enter your Username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" placeholder="Enter your Password" required>
<div class="wrap">
<button type="submit">Submit</button>
</div>
</form>
<p>Not registered?
<a href="signup" style="text-decoration: none;">
Create an account
</a>
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,63 @@
body {
background-color: whitesmoke;
}
table.datatable {
border: thin solid;
border-collapse: collapse;
}
table.datatable th, td {
border: thin solid;
border-collapse: collapse;
padding: 5px;
}
table.datatable tbody tr:nth-child(odd) {
background-color: #ffffff;
}
table.formtable {
border: thin solid;
border-collapse: collapse;
}
table.formtable th, td {
border: thin solid;
border-collapse: collapse;
padding: 5px;
}
ul.mainmenu {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333333;
}
ul.mainmenu li {
float: left;
}
ul.mainmenu li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
ul.mainmenu li a:hover {
background-color: #111111;
}

View File

@@ -0,0 +1,86 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhcgi_service" >
<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>
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags-INACTIVE" >
--api=AQHOME_API
</setVar>
<setVar name="local/typefiles" >
module.t2d
moduleperms.t2d
role.t2d
user.t2d
session.t2d
permdef.t2d
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="false" install="$(pkgincludedir)/service" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/service" >
service.h
</headers>
<headers dist="true" >
service_p.h
</headers>
<sources>
$(local/typefiles)
service.c
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,20 @@
- service:
- userList
- rootModule
- sessionList
- user:
- modulePerms
- module:
- roleList
- permDefList
- session

View File

@@ -0,0 +1,103 @@
<?xml?>
<tm2>
<type id="AQH_MODULE" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_MODULE</identifier>
<prefix>AQH_Module</prefix>
<baseFileName>module</baseFileName>
<flags>
with_inherit
with_xml
with_db
with_list1
with_list2
with_tree2
nodup
nocopy
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">gwenhywfar/error.h</header>
<header type="sys" loc="post">aqhome-cgi/service/role.h</header>
<header type="sys" loc="post">aqhome-cgi/service/permdef.h</header>
</headers>
<inlines>
</inlines>
</lang>
<defines>
<define id="AQH_MODULE_FLAGS" prefix="AQH_MODULE_FLAGS_">
<item name="ACTIVE" value="0x00000001" />
</define>
</defines>
<members>
<member name="id" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="flags" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_flags</flags>
</member>
<member name="name" type="char_ptr" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="descr" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="guestPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="roleList" type="AQH_ROLE_LIST" elementName="role" >
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<getflags>none</getflags>
<setflags>none</setflags>
</member>
<member name="permDefList" type="AQH_PERMDEF_LIST" elementName="permdef" >
<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,67 @@
<?xml?>
<tm2>
<type id="AQH_MODULE_PERMS" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_MODULE_PERMS</identifier>
<prefix>AQH_ModulePerms</prefix>
<baseFileName>moduleperms</baseFileName>
<flags>
with_inherit
with_xml
with_db
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">gwenhywfar/error.h</header>
<header type="sys" loc="post">aqhome-cgi/service/role.h</header>
</headers>
<inlines>
</inlines>
</lang>
<members>
<member name="moduleId" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="explAddPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="explDelPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="roleArray" type="uint8_t_array" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,65 @@
<?xml?>
<tm2>
<type id="AQH_PERMDEF" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_PERMDEF</identifier>
<prefix>AQH_PermDef</prefix>
<baseFileName>permdef</baseFileName>
<flags>
with_xml
with_db
with_list1
nodup
nocopy
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
</headers>
<inlines>
</inlines>
</lang>
<members>
<member name="id" type="char_ptr" maxlen="32">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="name" type="char_ptr" maxlen="32">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="mask" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="descr" type="char_ptr" maxlen="256">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,79 @@
<?xml?>
<tm2>
<type id="AQH_ROLE" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_ROLE</identifier>
<prefix>AQH_Role</prefix>
<baseFileName>role</baseFileName>
<flags>
with_xml
with_db
with_list1
with_list2
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">gwenhywfar/error.h</header>
</headers>
<inlines>
</inlines>
</lang>
<members>
<member name="id" type="uint8_t" maxlen="1">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember</flags>
</member>
<member name="name" type="char_ptr" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="descr" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="perms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="explAddPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="explDelPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,414 @@
/****************************************************************************
* 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
//#define DISABLE_DEBUGLOG
#include "aqhome-cgi/service/service_p.h"
#include <gwenhywfar/stringlist.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
GWEN_INHERIT_FUNCTIONS(AQH_SERVICE);
GWEN_LIST_FUNCTIONS(AQH_SERVICE, AQH_Service);
AQH_SERVICE *AQH_Service_new(const char *baseFolder, const char *baseUrl)
{
AQH_SERVICE *sv;
GWEN_NEW_OBJECT(AQH_SERVICE, sv);
GWEN_INHERIT_INIT(AQH_SERVICE, sv);
GWEN_LIST_INIT(AQH_SERVICE, sv);
sv->baseUrl=baseUrl?strdup(baseUrl):NULL;
sv->baseFolder=baseFolder?strdup(baseFolder):NULL;
if (sv->baseFolder) {
GWEN_BUFFER *dbuf;
uint32_t pos;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "%s%s", sv->baseFolder, GWEN_DIR_SEPARATOR_S);
pos=GWEN_Buffer_GetPos(dbuf);
/* data folder */
GWEN_Buffer_AppendString(dbuf, "data");
sv->runtimeFolder=strdup(GWEN_Buffer_GetStart(dbuf));
DBG_ERROR(NULL, "Runtime folder: %s", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_Crop(dbuf, 0, pos);
/* cache folder */
GWEN_Buffer_AppendString(dbuf, "cache");
sv->cacheFolder=strdup(GWEN_Buffer_GetStart(dbuf));
DBG_ERROR(NULL, "Cache folder: %s", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_Crop(dbuf, 0, pos);
GWEN_Buffer_free(dbuf);
}
return sv;
}
void AQH_Service_free(AQH_SERVICE *sv)
{
if (sv) {
GWEN_LIST_FINI(AQH_SERVICE, sv);
GWEN_INHERIT_FINI(AQH_SERVICE, sv);
free(sv->baseUrl);
free(sv->baseFolder);
free(sv->runtimeFolder);
free(sv->cacheFolder);
GWEN_FREE_OBJECT(sv);
}
}
const char *AQH_Service_GetBaseUrl(const AQH_SERVICE *sv)
{
return sv?sv->baseUrl:NULL;
}
const char *AQH_Service_GetBaseFolder(const AQH_SERVICE *sv)
{
return sv?sv->baseFolder:NULL;
}
const char *AQH_Service_GetRuntimeFolder(const AQH_SERVICE *sv)
{
return sv?sv->runtimeFolder:NULL;
}
const char *AQH_Service_GetCacheFolder(const AQH_SERVICE *sv)
{
return sv?sv->cacheFolder:NULL;
}
#if 0
AQH_MODULE *AQH_Service_GetModuleByPath(const AQH_SERVICE *sv, const char *s)
{
if (sv && s && *s) {
GWEN_STRINGLIST *sl;
sl=GWEN_StringList_fromString2(s, "/", 0,
GWEN_TEXT_FLAGS_DEL_QUOTES |
GWEN_TEXT_FLAGS_DEL_LEADING_BLANKS |
GWEN_TEXT_FLAGS_DEL_MULTIPLE_BLANKS |
GWEN_TEXT_FLAGS_DEL_TRAILING_BLANKS);
if (sl) {
GWEN_STRINGLISTENTRY *se;
AQH_MODULE *m;
m=sv->rootModule;
se=GWEN_StringList_FirstEntry(sl);
while(se && m) {
const char *sWantedName;
sWantedName=GWEN_StringListEntry_Data(se);
if (sWantedName && *sWantedName) {
AQH_MODULE *mTemp;
mTemp=AQH_Module_Tree2_GetFirstChild(m);
while(mTemp) {
const char *sModName;
sModName=AQH_Module_GetName(mTemp);
if (sModName && *sModName && 0==strcasecmp(sModName, sWantedName))
break;
mTemp=AQH_Module_Tree2_GetNext(mTemp);
}
if (mTemp==NULL) {
DBG_ERROR(NULL, "Module \%s\" not found", sWantedName);
GWEN_StringList_free(sl);
return NULL;
}
m=mTemp;
}
else {
DBG_ERROR(NULL, "Empty path element in \"%s\"", s);
GWEN_StringList_free(sl);
return NULL;
}
se=GWEN_StringListEntry_Next(se);
} /* while(se && m) */
GWEN_StringList_free(sl);
return m;
}
else {
DBG_ERROR(NULL, "Bad path \"%s\"", s);
}
}
return NULL;
}
#endif
int AQH_Service_HandleRequest(AQH_SERVICE *sv, AQCGI_REQUEST *req)
{
return (sv && sv->handleRequestFn)?(sv->handleRequestFn(sv, req)):GWEN_ERROR_NOT_IMPLEMENTED;
}
AQH_USER *AQH_Service_LoadUser(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->loadUserFn)?(sv->loadUserFn(sv, s)):NULL;
}
int AQH_Service_SaveUser(AQH_SERVICE *sv, AQH_USER *user)
{
return (sv && sv->saveUserFn)?(sv->saveUserFn(sv, user)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_AddUser(AQH_SERVICE *sv, AQH_USER *user)
{
return (sv && sv->addUserFn)?(sv->addUserFn(sv, user)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_DelUser(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->delUserFn)?(sv->delUserFn(sv, s)):GWEN_ERROR_NOT_IMPLEMENTED;
}
GWEN_STRINGLIST *AQH_Service_ListUsers(AQH_SERVICE *sv)
{
return (sv && sv->listUsersFn)?(sv->listUsersFn(sv)):NULL;
}
AQH_MODULE *AQH_Service_LoadModule(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->loadModuleFn)?(sv->loadModuleFn(sv, s)):NULL;
}
int AQH_Service_SaveModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
return (sv && sv->saveModuleFn)?(sv->saveModuleFn(sv, module)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_AddModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
return (sv && sv->addModuleFn)?(sv->addModuleFn(sv, module)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_DelModule(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->delModuleFn)?(sv->delModuleFn(sv, s)):GWEN_ERROR_NOT_IMPLEMENTED;
}
GWEN_STRINGLIST *AQH_Service_ListModules(AQH_SERVICE *sv)
{
return (sv && sv->listModulesFn)?(sv->listModulesFn(sv)):NULL;
}
AQH_SESSION* AQH_Service_LoadSession(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->loadSessionFn)?(sv->loadSessionFn(sv, s)):NULL;
}
int AQH_Service_SaveSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
return (sv && sv->saveSessionFn)?(sv->saveSessionFn(sv, session)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_AddSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
return (sv && sv->addSessionFn)?(sv->addSessionFn(sv, session)):GWEN_ERROR_NOT_IMPLEMENTED;
}
int AQH_Service_DelSession(AQH_SERVICE *sv, const char *s)
{
return (sv && sv->delSessionFn)?(sv->delSessionFn(sv, s)):GWEN_ERROR_NOT_IMPLEMENTED;
}
void AQH_Service_SetHandleRequestFn(AQH_SERVICE *sv, AQH_SERVICE_HANDLEREQUEST_FN f)
{
if (sv)
sv->handleRequestFn=f;
}
void AQH_Service_SetLoadUserFn(AQH_SERVICE *sv, AQH_SERVICE_LOADUSER_FN f)
{
if (sv)
sv->loadUserFn=f;
}
void AQH_Service_SetSaveUserFn(AQH_SERVICE *sv, AQH_SERVICE_SAVEUSER_FN f)
{
if (sv)
sv->saveUserFn=f;
}
void AQH_Service_SetAddUserFn(AQH_SERVICE *sv, AQH_SERVICE_ADDUSER_FN f)
{
if (sv)
sv->addUserFn=f;
}
void AQH_Service_SetDelUserFn(AQH_SERVICE *sv, AQH_SERVICE_DELUSER_FN f)
{
if (sv)
sv->delUserFn=f;
}
void AQH_Service_SetListUsersFn(AQH_SERVICE *sv, AQH_SERVICE_LISTUSERS_FN f)
{
if (sv)
sv->listUsersFn=f;
}
void AQH_Service_SetLoadModuleFn(AQH_SERVICE *sv, AQH_SERVICE_LOADMODULE_FN f)
{
if (sv)
sv->loadModuleFn=f;
}
void AQH_Service_SetSaveModuleFn(AQH_SERVICE *sv, AQH_SERVICE_SAVEMODULE_FN f)
{
if (sv)
sv->saveModuleFn=f;
}
void AQH_Service_SetAddModuleFn(AQH_SERVICE *sv, AQH_SERVICE_ADDMODULE_FN f)
{
if (sv)
sv->addModuleFn=f;
}
void AQH_Service_SetDelModuleFn(AQH_SERVICE *sv, AQH_SERVICE_DELMODULE_FN f)
{
if (sv)
sv->delModuleFn=f;
}
void AQH_Service_SetListModulesFn(AQH_SERVICE *sv, AQH_SERVICE_LISTMODULES_FN f)
{
if (sv)
sv->listModulesFn=f;
}
void AQH_Service_SetLoadSessionFn(AQH_SERVICE *sv, AQH_SERVICE_LOADSESSION_FN f)
{
if (sv)
sv->loadSessionFn=f;
}
void AQH_Service_SetSaveSessionFn(AQH_SERVICE *sv, AQH_SERVICE_SAVESESSION_FN f)
{
if (sv)
sv->saveSessionFn=f;
}
void AQH_Service_SetAddSessionFn(AQH_SERVICE *sv, AQH_SERVICE_ADDSESSION_FN f)
{
if (sv)
sv->addSessionFn=f;
}
void AQH_Service_SetDelSessionFn(AQH_SERVICE *sv, AQH_SERVICE_DELSESSION_FN f)
{
if (sv)
sv->delSessionFn=f;
}
void AQH_Service_SetListSessionsFn(AQH_SERVICE *sv, AQH_SERVICE_LISTSESSIONS_FN f)
{
if (sv)
sv->listSessionsFn=f;
}

View File

@@ -0,0 +1,107 @@
/****************************************************************************
* 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_SERVICE_H
#define AQHOME_SERVICE_H
#include <aqhome/api.h>
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/list.h>
#include <gwenhywfar/stringlist.h>
typedef struct AQH_SERVICE AQH_SERVICE;
GWEN_INHERIT_FUNCTION_DEFS(AQH_SERVICE);
GWEN_LIST_FUNCTION_DEFS(AQH_SERVICE, AQH_Service);
#include "aqhome-cgi/service/user.h"
#include "aqhome-cgi/service/module.h"
#include "aqhome-cgi/service/session.h"
#include <aqcgi/cgi.h>
typedef int (*AQH_SERVICE_HANDLEREQUEST_FN)(AQH_SERVICE *sv, AQCGI_REQUEST *req);
typedef AQH_USER* (*AQH_SERVICE_LOADUSER_FN)(AQH_SERVICE *sv, const char *s);
typedef int (*AQH_SERVICE_SAVEUSER_FN)(AQH_SERVICE *sv, AQH_USER *user);
typedef int (*AQH_SERVICE_ADDUSER_FN)(AQH_SERVICE *sv, AQH_USER *user);
typedef int (*AQH_SERVICE_DELUSER_FN)(AQH_SERVICE *sv, const char *s);
typedef GWEN_STRINGLIST* (*AQH_SERVICE_LISTUSERS_FN)(AQH_SERVICE *sv);
typedef AQH_MODULE* (*AQH_SERVICE_LOADMODULE_FN)(AQH_SERVICE *sv, const char *s);
typedef int (*AQH_SERVICE_SAVEMODULE_FN)(AQH_SERVICE *sv, AQH_MODULE *module);
typedef int (*AQH_SERVICE_ADDMODULE_FN)(AQH_SERVICE *sv, AQH_MODULE *module);
typedef int (*AQH_SERVICE_DELMODULE_FN)(AQH_SERVICE *sv, const char *s);
typedef GWEN_STRINGLIST* (*AQH_SERVICE_LISTMODULES_FN)(AQH_SERVICE *sv);
typedef AQH_SESSION* (*AQH_SERVICE_LOADSESSION_FN)(AQH_SERVICE *sv, const char *s);
typedef int (*AQH_SERVICE_SAVESESSION_FN)(AQH_SERVICE *sv, AQH_SESSION *session);
typedef int (*AQH_SERVICE_ADDSESSION_FN)(AQH_SERVICE *sv, AQH_SESSION *session);
typedef int (*AQH_SERVICE_DELSESSION_FN)(AQH_SERVICE *sv, const char *s);
typedef GWEN_STRINGLIST* (*AQH_SERVICE_LISTSESSIONS_FN)(AQH_SERVICE *sv);
AQH_SERVICE *AQH_Service_new(const char *baseFolder, const char *baseUrl);
void AQH_Service_free(AQH_SERVICE *sv);
const char *AQH_Service_GetBaseUrl(const AQH_SERVICE *sv);
const char *AQH_Service_GetBaseFolder(const AQH_SERVICE *sv);
const char *AQH_Service_GetRuntimeFolder(const AQH_SERVICE *sv);
const char *AQH_Service_GetCacheFolder(const AQH_SERVICE *sv);
int AQH_Service_HandleRequest(AQH_SERVICE *sv, AQCGI_REQUEST *req);
AQH_USER *AQH_Service_LoadUser(AQH_SERVICE *sv, const char *s);
int AQH_Service_SaveUser(AQH_SERVICE *sv, AQH_USER *user);
int AQH_Service_AddUser(AQH_SERVICE *sv, AQH_USER *user);
int AQH_Service_DelUser(AQH_SERVICE *sv, const char *s);
GWEN_STRINGLIST *AQH_Service_ListUsers(AQH_SERVICE *sv);
AQH_MODULE *AQH_Service_LoadModule(AQH_SERVICE *sv, const char *s);
int AQH_Service_SaveModule(AQH_SERVICE *sv, AQH_MODULE *module);
int AQH_Service_AddModule(AQH_SERVICE *sv, AQH_MODULE *module);
int AQH_Service_DelModule(AQH_SERVICE *sv, const char *s);
GWEN_STRINGLIST *AQH_Service_ListModules(AQH_SERVICE *sv);
AQH_SESSION* AQH_Service_LoadSession(AQH_SERVICE *sv, const char *s);
int AQH_Service_SaveSession(AQH_SERVICE *sv, AQH_SESSION *session);
int AQH_Service_AddSession(AQH_SERVICE *sv, AQH_SESSION *session);
int AQH_Service_DelSession(AQH_SERVICE *sv, const char *s);
GWEN_STRINGLIST *AQH_Service_ListSessions(AQH_SERVICE *sv);
void AQH_Service_SetHandleRequestFn(AQH_SERVICE *sv, AQH_SERVICE_HANDLEREQUEST_FN f);
void AQH_Service_SetLoadUserFn(AQH_SERVICE *sv, AQH_SERVICE_LOADUSER_FN f);
void AQH_Service_SetSaveUserFn(AQH_SERVICE *sv, AQH_SERVICE_SAVEUSER_FN f);
void AQH_Service_SetAddUserFn(AQH_SERVICE *sv, AQH_SERVICE_ADDUSER_FN f);
void AQH_Service_SetDelUserFn(AQH_SERVICE *sv, AQH_SERVICE_DELUSER_FN f);
void AQH_Service_SetListUsersFn(AQH_SERVICE *sv, AQH_SERVICE_LISTUSERS_FN f);
void AQH_Service_SetLoadModuleFn(AQH_SERVICE *sv, AQH_SERVICE_LOADMODULE_FN f);
void AQH_Service_SetSaveModuleFn(AQH_SERVICE *sv, AQH_SERVICE_SAVEMODULE_FN f);
void AQH_Service_SetAddModuleFn(AQH_SERVICE *sv, AQH_SERVICE_ADDMODULE_FN f);
void AQH_Service_SetDelModuleFn(AQH_SERVICE *sv, AQH_SERVICE_DELMODULE_FN f);
void AQH_Service_SetListModulesFn(AQH_SERVICE *sv, AQH_SERVICE_LISTMODULES_FN f);
void AQH_Service_SetLoadSessionFn(AQH_SERVICE *sv, AQH_SERVICE_LOADSESSION_FN f);
void AQH_Service_SetSaveSessionFn(AQH_SERVICE *sv, AQH_SERVICE_SAVESESSION_FN f);
void AQH_Service_SetAddSessionFn(AQH_SERVICE *sv, AQH_SERVICE_ADDSESSION_FN f);
void AQH_Service_SetDelSessionFn(AQH_SERVICE *sv, AQH_SERVICE_DELSESSION_FN f);
void AQH_Service_SetListSessionsFn(AQH_SERVICE *sv, AQH_SERVICE_LISTSESSIONS_FN f);
#endif

View File

@@ -0,0 +1,50 @@
/****************************************************************************
* 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_SERVICE_P_H
#define AQHOME_SERVICE_P_H
#include "aqhome-cgi/service/service.h"
struct AQH_SERVICE {
GWEN_INHERIT_ELEMENT(AQH_SERVICE);
GWEN_LIST_ELEMENT(AQH_SERVICE);
char *baseUrl;
char *baseFolder;
char *runtimeFolder;
char *cacheFolder;
AQH_SERVICE_HANDLEREQUEST_FN handleRequestFn;
AQH_SERVICE_LOADUSER_FN loadUserFn;
AQH_SERVICE_SAVEUSER_FN saveUserFn;
AQH_SERVICE_ADDUSER_FN addUserFn;
AQH_SERVICE_DELUSER_FN delUserFn;
AQH_SERVICE_LISTUSERS_FN listUsersFn;
AQH_SERVICE_LOADMODULE_FN loadModuleFn;
AQH_SERVICE_SAVEMODULE_FN saveModuleFn;
AQH_SERVICE_ADDMODULE_FN addModuleFn;
AQH_SERVICE_DELMODULE_FN delModuleFn;
AQH_SERVICE_LISTMODULES_FN listModulesFn;
AQH_SERVICE_LOADSESSION_FN loadSessionFn;
AQH_SERVICE_SAVESESSION_FN saveSessionFn;
AQH_SERVICE_ADDSESSION_FN addSessionFn;
AQH_SERVICE_DELSESSION_FN delSessionFn;
AQH_SERVICE_LISTSESSIONS_FN listSessionsFn;
};
#endif

View File

@@ -0,0 +1,117 @@
<?xml?>
<tm2>
<type id="AQH_SESSION" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_SESSION</identifier>
<prefix>AQH_Session</prefix>
<baseFileName>session</baseFileName>
<flags>
with_xml
with_db
with_list1
with_list2
nodup
nocopy
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">gwenhywfar/error.h</header>
<header type="sys" loc="pre">gwenhywfar/timestamp.h</header>
<header type="sys" loc="post">aqhome-cgi/service/user.h</header>
</headers>
<inlines>
</inlines>
</lang>
<defines>
<define id="AQH_SESSION_RTFLAGS" prefix="AQH_SESSION_RTFLAGS_">
<item name="MODIFIED" value="0x00000001" />
</define>
</defines>
<members>
<member name="uid" type="char_ptr" maxlen="64">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember</flags>
</member>
<member name="flags" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_flags</flags>
</member>
<member name="userAlias" type="char_ptr" maxlen="64">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="state" type="int" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="tempToken" type="char_ptr" maxlen="64">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="timestampCreation" type="gwen_timestamp" maxlen="8">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="timestampLastAccess" type="gwen_timestamp" maxlen="8">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="user" type="AQH_USER">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<setflags>assign</setflags>
<getflags>none</getflags>
<flags>volatile nodup nocopy</flags>
</member>
<member name="runtimeFlags" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_flags volatile</flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,164 @@
<?xml?>
<tm2>
<type id="AQH_USER" type="pointer">
<descr>
</descr>
<lang id="c">
<identifier>AQH_USER</identifier>
<prefix>AQH_User</prefix>
<baseFileName>user</baseFileName>
<flags>
with_xml
with_db
with_list1
with_list2
with_refcount
nodup
nocopy
</flags>
<headers>
<header type="sys" loc="pre">aqhome/api.h</header>
<header type="sys" loc="pre">gwenhywfar/error.h</header>
<header type="sys" loc="pre">gwenhywfar/timestamp.h</header>
<header type="sys" loc="post">aqhome-cgi/service/moduleperms.h</header>
</headers>
<inlines>
</inlines>
</lang>
<defines>
<define id="AQH_USER_FLAGS" prefix="AQH_USER_FLAGS_">
<item name="ADMIN" value="0x00000001" />
</define>
<define id="AQH_USER_RTFLAGS" prefix="AQH_USER_RTFLAGS_">
<item name="MODIFIED" value="0x00000001" />
<item name="PERMSCALC" value="0x00000002" />
</define>
</defines>
<enums>
<enum id="AQH_USER_STATE" prefix="AQH_UserState_">
<item name="suspended">
<descr>User suspended</descr>
</item>
<item name="waitForConfirmation">
<descr>Waiting for email confirmation</descr>
</item>
<item name="waitForApproval">
<descr>Waiting for approval by admin</descr>
</item>
<item name="active">
<descr>User active</descr>
</item>
</enum>
</enums>
<members>
<member name="id" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="flags" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_flags</flags>
</member>
<member name="state" type="int" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>sortbymember</flags>
</member>
<member name="name" type="char_ptr" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own sortbymember</flags>
</member>
<member name="alias" type="char_ptr" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="hashedPassword" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="email" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="notes" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
</member>
<member name="timestampCreation" type="gwen_timestamp" maxlen="8">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own sortbymember</flags>
</member>
<member name="timestampLastLogin" type="gwen_timestamp" maxlen="8">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own sortbymember</flags>
</member>
<member name="modulePermList" type="AQH_MODULE_PERMS_LIST">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<getflags>none</getflags>
<setflags>none</setflags>
<flags>own</flags>
</member>
<member name="runtimeFlags" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>volatile with_flags</flags>
</member>
</members>
</type>
</tm2>

View File

@@ -0,0 +1,612 @@
/****************************************************************************
* 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
//#define DISABLE_DEBUGLOG
#include "./service_file_p.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define AQH_SERVICE_FILE_GROUP_USERS "users"
#define AQH_SERVICE_FILE_GROUP_MODULES "modules"
#define AQH_SERVICE_FILE_GROUP_SESSIONS "sessions"
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
GWEN_INHERIT(AQH_SERVICE, AQH_SERVICE_FILE)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static AQH_USER *_loadUser(AQH_SERVICE *sv, const char *s);
static int _saveUser(AQH_SERVICE *sv, AQH_USER *user);
static int _addUser(AQH_SERVICE *sv, AQH_USER *user);
static int _delUser(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listUsers(AQH_SERVICE *sv);
static AQH_MODULE *_loadModule(AQH_SERVICE *sv, const char *s);
static int _saveModule(AQH_SERVICE *sv, AQH_MODULE *module);
static int _addModule(AQH_SERVICE *sv, AQH_MODULE *module);
static int _delModule(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listModules(AQH_SERVICE *sv);
static AQH_SESSION* _loadSession(AQH_SERVICE *sv, const char *s);
static int _saveSession(AQH_SERVICE *sv, AQH_SESSION *session);
static int _addSession(AQH_SERVICE *sv, AQH_SESSION *session);
static int _delSession(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listSessions(AQH_SERVICE *sv);
static GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName);
static int _saveGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
static int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
static int _deleteGroup(AQH_SERVICE *sv, const char *groupName, const char *subGroupName);
static GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName);
//static void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder, const char *baseUrl)
{
AQH_SERVICE *sv;
AQH_SERVICE_FILE *xs;
GWEN_BUFFER *dbuf;
sv=AQH_Service_new(baseFolder, baseUrl);
GWEN_NEW_OBJECT(AQH_SERVICE_FILE, xs);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv, xs, _freeData);
AQH_Service_SetLoadUserFn(sv, _loadUser);
AQH_Service_SetSaveUserFn(sv, _saveUser);
AQH_Service_SetAddUserFn(sv, _addUser);
AQH_Service_SetDelUserFn(sv, _delUser);
AQH_Service_SetListUsersFn(sv, _listUsers);
AQH_Service_SetLoadModuleFn(sv, _loadModule);
AQH_Service_SetSaveModuleFn(sv, _saveModule);
AQH_Service_SetAddModuleFn(sv, _addModule);
AQH_Service_SetDelModuleFn(sv, _delModule);
AQH_Service_SetListModulesFn(sv, _listModules);
AQH_Service_SetLoadSessionFn(sv, _loadSession);
AQH_Service_SetSaveSessionFn(sv, _saveSession);
AQH_Service_SetAddSessionFn(sv, _addSession);
AQH_Service_SetDelSessionFn(sv, _delSession);
AQH_Service_SetListSessionsFn(sv, _listSessions);
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "dir://%s", AQH_Service_GetRuntimeFolder(sv));
DBG_ERROR(NULL, "Creating config mgr \"%s\"", GWEN_Buffer_GetStart(dbuf));
xs->configMgr=GWEN_ConfigMgr_Factory(GWEN_Buffer_GetStart(dbuf));
if (xs->configMgr==NULL) {
DBG_ERROR(NULL, "Error creating config mgr \"%s\"", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
AQH_Service_free(sv);
return NULL;
}
GWEN_Buffer_free(dbuf);
return sv;
}
void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
{
AQH_SERVICE_FILE *xs;
xs=(AQH_SERVICE_FILE*) p;
GWEN_ConfigMgr_free(xs->configMgr);
GWEN_FREE_OBJECT(xs);
}
AQH_USER *_loadUser(AQH_SERVICE *sv, const char *s)
{
GWEN_DB_NODE *db;
db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s);
if (db) {
AQH_USER *user;
user=AQH_User_fromDb(db);
GWEN_DB_Group_free(db);
return user;
}
return NULL;
}
int _saveUser(AQH_SERVICE *sv, AQH_USER *user)
{
const char *s;
s=AQH_User_GetAlias(user);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_User_toDb(user, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
//_logGroup(AQH_SERVICE_FILE_GROUP_USERS, s, db);
rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
return GWEN_ERROR_INVALID;
}
int _addUser(AQH_SERVICE *sv, AQH_USER *user)
{
const char *s;
s=AQH_User_GetAlias(user);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_User_toDb(user, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
else {
DBG_ERROR(NULL, "Missing alias");
}
return GWEN_ERROR_INVALID;
}
int _delUser(AQH_SERVICE *sv, const char *s)
{
return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_USERS, s);
}
GWEN_STRINGLIST *_listUsers(AQH_SERVICE *sv)
{
return _listGroup(sv, AQH_SERVICE_FILE_GROUP_USERS);
}
AQH_MODULE *_loadModule(AQH_SERVICE *sv, const char *s)
{
GWEN_DB_NODE *db;
db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s);
if (db) {
AQH_MODULE *module;
module=AQH_Module_fromDb(db);
GWEN_DB_Group_free(db);
return module;
}
return NULL;
}
int _saveModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
const char *s;
s=AQH_Module_GetName(module);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_Module_toDb(module, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
return GWEN_ERROR_INVALID;
}
int _addModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
const char *s;
s=AQH_Module_GetName(module);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_Module_toDb(module, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
return GWEN_ERROR_INVALID;
}
int _delModule(AQH_SERVICE *sv, const char *s)
{
return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_MODULES, s);
}
GWEN_STRINGLIST *_listModules(AQH_SERVICE *sv)
{
return _listGroup(sv, AQH_SERVICE_FILE_GROUP_MODULES);
}
AQH_SESSION* _loadSession(AQH_SERVICE *sv, const char *s)
{
GWEN_DB_NODE *db;
db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s);
if (db) {
AQH_SESSION *session;
session=AQH_Session_fromDb(db);
GWEN_DB_Group_free(db);
return session;
}
return NULL;
}
int _saveSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
const char *s;
s=AQH_Session_GetUid(session);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_Session_toDb(session, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
return GWEN_ERROR_INVALID;
}
int _addSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
const char *s;
s=AQH_Session_GetUid(session);
if (s && *s) {
GWEN_DB_NODE *db;
int rv;
db=GWEN_DB_Group_new(s);
rv=AQH_Session_toDb(session, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
GWEN_DB_Group_free(db);
return rv;
}
GWEN_DB_Group_free(db);
return 0;
}
return GWEN_ERROR_INVALID;
}
int _delSession(AQH_SERVICE *sv, const char *s)
{
return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s);
}
GWEN_STRINGLIST *_listSessions(AQH_SERVICE *sv)
{
return _listGroup(sv, AQH_SERVICE_FILE_GROUP_SESSIONS);
}
GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName)
{
DBG_ERROR(NULL, "Lock and load group %s/%s", groupName, subGroupName);
if (sv && groupName && subGroupName) {
AQH_SERVICE_FILE *xs;
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
GWEN_DB_NODE *db=NULL;
int rv;
DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
return NULL;
}
DBG_ERROR(NULL, "Loading group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_GetGroup(xs->configMgr, groupName, subGroupName, &db);
if (rv<0) {
DBG_ERROR(NULL, "Error reading group \"%s/%s\": %d", groupName, subGroupName, rv);
GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
return NULL;
}
DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
GWEN_DB_Group_free(db);
return NULL;
}
return db;
}
}
else {
DBG_ERROR(NULL, "Missing argument");
}
return NULL;
}
int _saveGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
if (sv && groupName && subGroupName && db) {
AQH_SERVICE_FILE *xs;
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
int rv;
DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
DBG_ERROR(NULL, "Writing group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_SetGroup(xs->configMgr, groupName, subGroupName, db);
if (rv<0) {
DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
return 0;
}
}
return GWEN_ERROR_INVALID;
}
int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
if (sv && groupName && subGroupName && db) {
AQH_SERVICE_FILE *xs;
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
int rv;
rv=GWEN_ConfigMgr_HasGroup(xs->configMgr, groupName, subGroupName);
if (rv>=0) {
DBG_ERROR(NULL, "Group \"%s/%s\" already exists", groupName, subGroupName);
return GWEN_ERROR_FOUND;
}
rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
rv=GWEN_ConfigMgr_SetGroup(xs->configMgr, groupName, subGroupName, db);
if (rv<0) {
DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
return 0;
}
}
return GWEN_ERROR_INVALID;
}
int _deleteGroup(AQH_SERVICE *sv, const char *groupName, const char *subGroupName)
{
if (sv && groupName && subGroupName) {
AQH_SERVICE_FILE *xs;
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
int rv;
rv=GWEN_ConfigMgr_DeleteGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
return 0;
}
}
return GWEN_ERROR_INVALID;
}
GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName)
{
if (sv && groupName) {
AQH_SERVICE_FILE *xs;
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
GWEN_STRINGLIST *sl;
int rv;
sl=GWEN_StringList_new();
rv=GWEN_ConfigMgr_ListSubGroups(xs->configMgr, groupName, sl);
if (rv<0) {
DBG_ERROR(NULL, "Error listing group \"%s\": %d", groupName, rv);
GWEN_StringList_free(sl);
return NULL;
}
if (GWEN_StringList_Count(sl)==0) {
GWEN_StringList_free(sl);
return NULL;
}
return sl;
}
}
return NULL;
}
#if 0
void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
if (db) {
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_DB_WriteToBuffer(db, dbuf, GWEN_DB_FLAGS_DEFAULT);
DBG_ERROR(NULL, "Group %s/%s:\n%s", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>",
GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
else {
DBG_ERROR(NULL, "Group %s/%s empty", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>");
}
}
#endif

View File

@@ -0,0 +1,21 @@
/****************************************************************************
* 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_SERVICE_FILE_H
#define AQHOME_SERVICE_FILE_H
#include "aqhome-cgi/service/service.h"
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder, const char *baseUrl);
#endif

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_SERVICE_FILE_P_H
#define AQHOME_SERVICE_FILE_P_H
#include "aqhome-cgi/service_file.h"
#include <gwenhywfar/configmgr.h>
typedef struct AQH_SERVICE_FILE AQH_SERVICE_FILE;
struct AQH_SERVICE_FILE {
GWEN_CONFIGMGR *configMgr;
};
#endif

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>

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

@@ -0,0 +1,268 @@
/****************************************************************************
* 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);
AqHomeDataServer_Fini(aqh);
AQH_Object_free(aqh);
AQH_EventLoop_free(eventLoop);
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,232 @@
/****************************************************************************
* 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_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 1024
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 512
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value,
int mode,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId);
static int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsLast(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);
/* ------------------------------------------------------------------------------------------------
* 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;
int mode;
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);
mode=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_MODE, AQH_MSGDATA_GETDATA_MODE_FIRST);
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
if (value) {
int resultCode;
resultCode=_getAndSendDataPoints(xo->storage, ep, value, mode, 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,
int mode,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
switch(mode) {
case AQH_MSGDATA_GETDATA_MODE_FIRST: return _getAndSendDataPointsFirst(storage, ep, value, num, refMsgId);
case AQH_MSGDATA_GETDATA_MODE_PERIOD: return _getAndSendDataPointsPeriod(storage, ep, value, tsBegin, tsEnd, num, refMsgId);
default:
case AQH_MSGDATA_GETDATA_MODE_LAST: return _getAndSendDataPointsLast(storage, ep, value, num, refMsgId);
}
}
int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, 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;
}
}
int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
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;
}
}
int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetFirstNDataPoints(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;
numTableEntries=(int)(tablePtr[0]);
numDataPoints=numTableEntries/2;
tablePtr++;
while(numDataPoints) {
AQH_MESSAGE *outMsg;
int toSend;
uint32_t flags=0;
toSend=numDataPoints;
if (toSend>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
toSend=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
numDataPoints-=toSend;
if (numDataPoints==0)
flags|=AQH_MSGDATA_MULTIDATA_FLAGS_LASTMSG;
outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags,
value, tablePtr, toSend);
tablePtr+=(toSend*2);
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,172 @@
/****************************************************************************
* 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_getdevices.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>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_DEVICESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_DEVICE_LIST *_getMatchingDeviceList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList);
static int _deviceMatches(const AQH_DEVICE *dev, const char *deviceName);
static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t refMsgId);
static void _sendDeviceListMsg(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)
{
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
AQH_DEVICE_LIST *deviceList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_INFO(NULL, "HandleGetDevices");
deviceList=_getMatchingDeviceList(xo, tagList);
if (deviceList) {
_sendDeviceList(ep, deviceList, refMsgId);
AQH_Device_List_free(deviceList);
}
else {
/* empty list */
_sendDeviceListMsg(ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
}
}
}
AQH_DEVICE_LIST *_getMatchingDeviceList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList)
{
const AQH_DEVICE_LIST *origDeviceList;
AQH_DEVICE_LIST *tmpDeviceList=NULL;
char *deviceName;
deviceName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDEVICES_TAGS_DEVICENAME, NULL):NULL;
origDeviceList=AQH_Storage_GetDeviceList(xo->storage);
if (origDeviceList) {
const AQH_DEVICE *dev;
tmpDeviceList=AQH_Device_List_new();
dev=AQH_Device_List_First(origDeviceList);
while(dev) {
if (_deviceMatches(dev, deviceName)) {
AQH_DEVICE *copyOfDevice;
copyOfDevice=AQH_Device_dup(dev);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
}
dev=AQH_Device_List_Next(dev);
}
if (AQH_Device_List_GetCount(tmpDeviceList)<1) {
AQH_Device_List_free(tmpDeviceList);
tmpDeviceList=NULL;
}
}
free(deviceName);
return tmpDeviceList;
}
int _deviceMatches(const AQH_DEVICE *dev, const char *deviceName)
{
if (deviceName && *deviceName) {
const char *s;
s=AQH_Device_GetNameForSystem(dev);
if (s && *s && GWEN_Text_ComparePattern(s, deviceName, 0)==-1)
return 0;
}
return 1;
}
void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *deviceList, uint32_t refMsgId)
{
AQH_DEVICE_LIST *tmpDeviceList;
const AQH_DEVICE *dev;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpDeviceList=AQH_Device_List_new();
dev=AQH_Device_List_First(deviceList);
while(dev) {
const AQH_DEVICE *next;
AQH_DEVICE *copyOfDevice;
next=AQH_Device_List_Next(dev);
copyOfDevice=AQH_Device_dup(dev);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceListMsg(ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
AQH_Device_List_Clear(tmpDeviceList);
}
dev=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceListMsg(ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
void _sendDeviceListMsg(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;
DBG_INFO(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

@@ -6,24 +6,22 @@
* should have received along with this file.
****************************************************************************/
#ifndef AQH_MSG_ENDPOINT_MGR_P_H
#define AQH_MSG_ENDPOINT_MGR_P_H
#ifndef AQHOME_DATA_S_GETDEVICES_H
#define AQHOME_DATA_S_GETDEVICES_H
#include <aqhome/api.h>
#include "./server.h"
#include "aqhome/msg/endpointmgr.h"
#include <gwenhywfar/tag16.h>
typedef struct AQH_MSG_ENDPOINT_MGR AQH_MSG_ENDPOINT_MGR;
struct AQH_MSG_ENDPOINT_MGR {
uint8_t busAddr;
};
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList);
#endif

View File

@@ -0,0 +1,183 @@
/****************************************************************************
* 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/aqhome.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_getvalues.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/text.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_VALUESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_VALUE_LIST *_getMatchingValueList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList);
static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t refMsgId);
static void _sendValueListMsg(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
static int _valueMatches(const AQH_VALUE *v, const char *deviceName, int modality);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
AQH_VALUE_LIST *valueList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_INFO(NULL, "HandleGetValues");
valueList=_getMatchingValueList(xo, tagList);
if (valueList) {
_sendValueList(ep, valueList, refMsgId);
AQH_Value_List_free(valueList);
}
else {
/* empty list */
_sendValueListMsg(ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
}
}
}
AQH_VALUE_LIST *_getMatchingValueList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList)
{
const AQH_VALUE_LIST *origValueList;
AQH_VALUE_LIST *tmpValueList=NULL;
char *deviceName;
int modality;
deviceName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETVALUES_TAGS_DEVICENAME, NULL):NULL;
modality=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETVALUES_TAGS_MODALITY, 0):0;
origValueList=AQH_Storage_GetValueList(xo->storage);
if (origValueList) {
const AQH_VALUE *v;
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(origValueList);
while(v) {
if (_valueMatches(v, deviceName, modality)) {
AQH_VALUE *copyOfValue;
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
}
v=AQH_Value_List_Next(v);
}
if (AQH_Value_List_GetCount(tmpValueList)<1) {
AQH_Value_List_free(tmpValueList);
tmpValueList=NULL;
}
}
free(deviceName);
return tmpValueList;
}
void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t refMsgId)
{
AQH_VALUE_LIST *tmpValueList;
const AQH_VALUE *v;
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(vl);
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));
_sendValueListMsg(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));
_sendValueListMsg(ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
void _sendValueListMsg(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;
DBG_INFO(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);
}
int _valueMatches(const AQH_VALUE *v, const char *deviceName, int modality)
{
if (modality!=AQH_ValueModality_Unknown) {
int valModality;
valModality=AQH_Value_GetModality(v);
if (valModality!=modality)
return 0;
}
if (deviceName && *deviceName) {
const char *s;
s=AQH_Value_GetDeviceNameForSystem(v);
if (s && *s && GWEN_Text_ComparePattern(s, deviceName, 0)==-1)
return 0;
}
return 1;
}

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,301 @@
/****************************************************************************
* 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 void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData);
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, double 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, double 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;
double valueData;
AQH_VALUE *systemValue;
valueName=AQH_Value_GetNameForSystem(recvdValue);
valueData=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, valueData);
AqHomeDataServer_AddRequestToTree(o, rq);
_storeDatapoint(xo, systemValue, valueData);
}
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);
} /* if recvdValue */
else {
DBG_ERROR(NULL, "No value in message");
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
}
}
void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData)
{
uint64_t timestamp;
int rv;
timestamp=(uint64_t) time(NULL);
rv=AQH_Storage_AddDatapoint(xo->storage, AQH_Value_GetId(v), timestamp, valueData);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
else {
DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, double 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, double 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, 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

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

@@ -0,0 +1,937 @@
/****************************************************************************
* 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 void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver,
const char *deviceName, const char *valueNameForSystem, const AQH_VALUE *valueTemplate);
static void _updateValue(AQHOME_SERVER *xo, AQH_VALUE *v, const char *deviceName, const AQH_VALUE *valueTemplate);
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);
AQH_MsgRequest_free(xo->requestTree);
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
* fini
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
void AqHomeDataServer_Fini(AQH_OBJECT *o)
{
AQHOME_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o);
if (xo) {
if (xo->pidFile)
remove(xo->pidFile);
}
}
/* 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_INFO(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_INFO(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)
_createValue(xo, epDriver, deviceName, GWEN_Buffer_GetStart(buf), valueTemplate);
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
return NULL;
}
}
else
_updateValue(xo, v, GWEN_Buffer_GetStart(buf), valueTemplate);
GWEN_Buffer_free(buf);
return v;
}
return NULL;
}
void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver,
const char *deviceName, const char *valueNameForSystem, const AQH_VALUE *valueTemplate)
{
AQH_DEVICE *device;
const char *serviceName;
const char *valueName;
AQH_VALUE *v;
serviceName=AQH_Endpoint_GetServiceName(epDriver);
valueName=AQH_Value_GetName(valueTemplate);
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
DBG_ERROR(AQH_LOGDOMAIN,
"Creating value: service=\"%s\", device=\"%s\", value \"%s\"",
serviceName?serviceName:"<no service>", deviceName?deviceName:"<no device>", valueName?valueName:"<no value>");
v=AQH_Value_new();
AQH_Value_SetDriver(v, serviceName);
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
AQH_Value_SetNameForSystem(v, valueNameForSystem);
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);
}
void _updateValue(AQHOME_SERVER *xo, AQH_VALUE *value, const char *deviceName, const AQH_VALUE *valueTemplate)
{
int chg=0;
const char *s1;
const char *s2;
int i1;
int i2;
DBG_INFO(AQH_LOGDOMAIN, "Updating value \"%s\"", deviceName);
s1=AQH_Value_GetValueUnits(valueTemplate);
s2=AQH_Value_GetValueUnits(value);
if (s1 && *s1) {
if (!(s2 && *s2) || (strcasecmp(s1, s2)!=0)) {
AQH_Value_SetValueUnits(value, s1);
chg=1;
}
}
i1=AQH_Value_GetValueType(valueTemplate);
i2=AQH_Value_GetValueType(value);
if (i1!=AQH_ValueDataType_Unknown) {
if (i1!=i2) {
AQH_Value_SetValueType(value, i1);
chg=1;
}
}
i1=AQH_Value_GetModality(valueTemplate);
i2=AQH_Value_GetModality(value);
if (i1!=AQH_ValueModality_Unknown) {
if (i1!=i2) {
AQH_Value_SetModality(value, i1);
chg=1;
}
}
if (chg)
AQH_Storage_AddRuntimeFlags(xo->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
}
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_ERROR(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;
}

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

@@ -0,0 +1,60 @@
/****************************************************************************
* 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);
void AqHomeDataServer_Fini(AQH_OBJECT *o);
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>

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

@@ -0,0 +1,283 @@
/****************************************************************************
* 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);
AQH_EventLoop_free(eventLoop);
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;
}

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