718 Commits

Author SHA1 Message Date
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
1114 changed files with 86005 additions and 38190 deletions

6
.gitignore vendored
View File

@@ -16,5 +16,11 @@ 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.*

7
0BUILD
View File

@@ -2,7 +2,7 @@
<gwbuild>
<project name="aqhome" version="0.0.4" so_current="0" so_age="0" so_revision="4" write_config_h="TRUE">
<project name="aqhome" version="0.0.19" so_current="0" so_age="0" so_revision="19" write_config_h="TRUE">
<setVar name="package">$(project_name)</setVar>
<setVar name="version">
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
@@ -51,6 +51,8 @@
<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>
@@ -126,6 +128,7 @@
</option>
<checkheaders>
signal.h
sys/stat.h
@@ -149,6 +152,8 @@
<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>

View File

@@ -8,6 +8,7 @@
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,684 @@
/****************************************************************************
* 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>
/* ------------------------------------------------------------------------------------------------
* 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;
}

View File

@@ -0,0 +1,85 @@
/****************************************************************************
* 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);
#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,99 @@
<?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
</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
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,395 @@
/****************************************************************************
* 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/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 _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},
{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);
}
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;
}
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;
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
* 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 <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);
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);
#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

@@ -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_INIT_H
#define AQHOME_CGI_MDEVICES_INIT_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>
int AQH_ModDevices_Create(AQH_SERVICE *sv);
#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: /aqbt/devices/value.html?device=");
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
GBAS(pbuf, "&value=");
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
}
else {
GBAS(pbuf, "Location: /aqbt/devices/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: /aqbt/devices/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,384 @@
/****************************************************************************
* 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>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define GBAS GWEN_Buffer_AppendString
#define GBAA GWEN_Buffer_AppendArgs
enum {
VALUEGRAPH_PERIOD_4H=1,
VALUEGRAPH_PERIOD_1D,
VALUEGRAPH_PERIOD_1W,
VALUEGRAPH_PERIOD_1M,
VALUEGRAPH_PERIOD_6M,
VALUEGRAPH_PERIOD_12M,
};
/* ------------------------------------------------------------------------------------------------
* 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 int _fileIsCurrent(const char *sPath, int seconds);
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 AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
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;
int rv;
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
if (!_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);
}
if (1) {
GWEN_BUFFER *ibuf;
ibuf=GWEN_Buffer_new(0, 1024, 0, 1);
// return file
rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fbuf), ibuf);
if (rv<0) {
DBG_ERROR(NULL, "Error reading \"%s\" (%d)", GWEN_Buffer_GetStart(fbuf), rv);
}
else {
GWEN_Buffer_Reset(dbuf);
GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf));
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: image/png");
AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE);
}
GWEN_Buffer_free(ibuf);
}
GWEN_Buffer_free(fbuf);
AQH_Value_free(value);
}
else {
DBG_ERROR(NULL, "Could not get value \"%s/%s\"", sDeviceName, sValueName);
}
}
int _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;
}
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=_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);
GWEN_Text_EscapeToBuffer(s, dbuf);
GBAA(dbuf, "-%s.png", graphParams->name);
}
AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
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, valueName, 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", valueName);
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;
}
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,26 @@
<!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="/aqbt/devices/index.html">Devices</a></li>
<li><a href="/aqbt/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="/aqbt/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,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

View File

@@ -35,40 +35,32 @@
</setVar>
<headers dist="true" >
aqhome_data.h
aqhome_data_p.h
fini.h
init.h
loop.h
c_connect.h
c_updatedata.h
c_getvalues.h
c_getdevices.h
c_getdatapoints.h
c_getlastdatapoint.h
c_setdata.h
c_addvalue.h
c_annvalue.h
c_moddevice.h
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)
aqhome_data.c
fini.c
init.c
loop.c
c_connect.c
c_updatedata.c
c_getvalues.c
c_getdevices.c
c_getdatapoints.c
c_getlastdatapoint.c
c_setdata.c
c_addvalue.c
c_annvalue.c
c_moddevice.c
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>

View File

@@ -1,152 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./aqhome_data_p.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
AQHOME_DATA *AqHomeData_new()
{
AQHOME_DATA *aqh;
GWEN_NEW_OBJECT(AQHOME_DATA, aqh);
aqh->storageMutex=GWEN_Mutex_new();
aqh->requestTree=GWEN_MsgRequest_new();
return aqh;
}
void AqHomeData_free(AQHOME_DATA *aqh)
{
if (aqh) {
GWEN_Mutex_free(aqh->storageMutex);
GWEN_MsgRequest_free(aqh->requestTree);
GWEN_MsgEndpoint_free(aqh->ipcdEndpoint);
GWEN_DB_Group_free(aqh->dbArgs);
AQH_Storage_free(aqh->storage);
free(aqh->pidFile);
GWEN_FREE_OBJECT(aqh);
}
}
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh)
{
return aqh?(aqh->ipcdEndpoint):NULL;
}
GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh)
{
return aqh?(aqh->dbArgs):NULL;
}
AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh)
{
return aqh?(aqh->storage):NULL;
}
GWEN_MSG_REQUEST *AqHomeData_GetRequestTree(const AQHOME_DATA *aqh)
{
return aqh?aqh->requestTree:NULL;
}
void AqHomeData_AddRequestToTree(AQHOME_DATA *aqh, GWEN_MSG_REQUEST *rq)
{
if (aqh && rq)
GWEN_MsgRequest_Tree2_AddChild(aqh->requestTree, rq);
}
const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh)
{
return aqh?aqh->pidFile:NULL;
}
int AqHomeData_GetTimeout(const AQHOME_DATA *aqh)
{
return aqh?aqh->timeout:0;
}
int AqHomeData_LockStorage(AQHOME_DATA *aqh)
{
int rv;
rv=GWEN_Mutex_Lock(aqh->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex");
return rv;
}
return rv;
}
int AqHomeData_UnlockStorage(AQHOME_DATA *aqh)
{
int rv;
rv=GWEN_Mutex_Unlock(aqh->storageMutex);
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex");
return rv;
}
return rv;
}
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcEndpointByServiceName(const AQHOME_DATA *aqh, const char *serviceName)
{
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
const char *s;
s=AQH_IpcEndpoint_GetServiceName(ep);
if (s && *s && strcasecmp(s, serviceName)==0)
return ep;
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
return NULL;
}

View File

@@ -1,47 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_H
#define AQHOME_DATA_H
#include "aqhome/data/storage.h"
#include <gwenhywfar/endpoint.h>
#include <gwenhywfar/request.h>
typedef struct AQHOME_DATA AQHOME_DATA;
AQHOME_DATA *AqHomeData_new();
void AqHomeData_free(AQHOME_DATA *aqh);
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh);
GWEN_MSG_ENDPOINT *AqHomeData_GetIpcEndpointByServiceName(const AQHOME_DATA *aqh, const char *serviceName);
GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh);
AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh);
const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh);
int AqHomeData_GetTimeout(const AQHOME_DATA *aqh);
int AqHomeData_LockStorage(AQHOME_DATA *aqh);
int AqHomeData_UnlockStorage(AQHOME_DATA *aqh);
GWEN_MSG_REQUEST *AqHomeData_GetRequestTree(const AQHOME_DATA *aqh);
void AqHomeData_AddRequestToTree(AQHOME_DATA *aqh, GWEN_MSG_REQUEST *rq);
#endif

View File

@@ -1,44 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_P_H
#define AQHOME_DATA_P_H
#include "./aqhome_data.h"
#include <gwenhywfar/mutex.h>
#define AQHOME_DATA_DEFAULT_PIDFILE "/var/run/aqhome-data.pid"
#define AQHOME_DATA_DEFAULT_DATADIR "/var/lib/aqhome-data/data"
#define AQHOME_DATA_DEFAULT_IPC_PORT 45456
#define AQHOME_DATA_STATEFILENAME "statefile"
struct AQHOME_DATA {
GWEN_MSG_ENDPOINT *ipcdEndpoint;
GWEN_DB_NODE *dbArgs;
AQH_STORAGE *storage;
char *pidFile;
int timeout; /* timeout for run e.g. inside valgrind */
GWEN_MUTEX *storageMutex;
GWEN_MSG_REQUEST *requestTree;
};
#endif

View File

@@ -1,72 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_addvalue.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleAddValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
AQH_VALUE *recvdValue;
AQH_ValuesDataIpcMsg_Parse(msg, 0);
recvdValue=AQH_ValuesDataIpcMsg_ReadFirstValue(msg);
if (recvdValue) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value==NULL)
resultCode=AQH_MSG_IPC_ERROR_PERMS;
AQH_Value_free(recvdValue);
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_ADDVALUE_H
#define AQHOME_DATA_C_ADDVALUE_H
#include "./aqhome_data.h"
void AqHomeData_HandleAddValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -1,65 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_annvalue.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleAnnounceValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
AQH_VALUE *recvdValue;
AQH_ValuesDataIpcMsg_Parse(msg, 0);
recvdValue=AQH_ValuesDataIpcMsg_ReadFirstValue(msg);
if (recvdValue) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Could not create announced value");
}
AQH_Value_free(recvdValue);
}
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2024 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_ANNVALUE_H
#define AQHOME_DATA_C_ANNVALUE_H
#include "./aqhome_data.h"
void AqHomeData_HandleAnnounceValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -1,93 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_connect.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/data/msg_data_connect.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleConnect(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
char *clientId=NULL;
char *userId=NULL;
char *passw=NULL;
uint32_t flags;
AQH_ConnectDataIpcMsg_Parse(msg, 0);
clientId=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_CLIENTID, NULL);
userId=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_USERID, NULL);
flags=AQH_Tag16IpcMsg_GetTagDataAsUint32(msg, AQH_MSGDATA_CONNECT_TAGS_FLAGS, 0);
passw=AQH_Tag16IpcMsg_GetTagDataAsNewString(msg, AQH_MSGDATA_CONNECT_TAGS_PASSWORD, NULL);
if (clientId)
AQH_IpcEndpoint_SetServiceName(ep, clientId);
if (userId)
AQH_IpcEndpoint_SetUserName(ep, userId);
if (flags & AQH_MSGDATA_CONNECT_FLAGS_WANTUPDATES)
GWEN_MsgEndpoint_AddFlags(ep, AQH_IPCENDPOINT_FLAGS_WANTUPDATES);
/* TODO: add user management, for now we allow all */
AQH_IpcEndpoint_SetPermissions(ep,
AQH_IPCENDPOINT_PERMS_LISTVALUES |
AQH_IPCENDPOINT_PERMS_READVALUE |
AQH_IPCENDPOINT_PERMS_ADDVALUE |
AQH_IPCENDPOINT_PERMS_LISTDATA |
AQH_IPCENDPOINT_PERMS_READDATA |
AQH_IPCENDPOINT_PERMS_ADDDATA |
AQH_IPCENDPOINT_PERMS_LISTDEVICES |
AQH_IPCENDPOINT_PERMS_READDEVICE |
AQH_IPCENDPOINT_PERMS_ADDDEVICE |
AQH_IPCENDPOINT_PERMS_MODDEVICE);
free(passw);
free(userId);
free(clientId);
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_CONNECT_H
#define AQHOME_DATA_C_CONNECT_H
#include "./aqhome_data.h"
void AqHomeData_HandleConnect(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -1,211 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_getdatapoints.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/data/msg_data_getdata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _getAndSendDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep,
const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsNoNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId);
static int _getAndSendDataPointsWithNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static void _sendDataPointsResponse(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId);
static void _getAndSendLastDatapoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_READDATA) {
AQH_VALUE *value;
char *valueName;
uint64_t tsBegin;
uint64_t tsEnd;
uint64_t numRequested;
AQH_GetDataDataIpcMsg_Parse(recvdMsg, 0);
valueName=AQH_Tag16IpcMsg_GetTagDataAsNewString(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
tsBegin=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
tsEnd=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_END, 0);
numRequested=AQH_Tag16IpcMsg_GetTagDataAsUint64(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
value=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName);
if (value) {
resultCode=_getAndSendDataPoints(aqh, ep, value, tsBegin, tsEnd, numRequested, GWEN_IpcMsg_GetMsgId(recvdMsg));
if (resultCode==AQH_MSG_IPC_SUCCESS)
return;
}
else {
DBG_INFO(NULL, "Value \"%s\" does not exist", valueName);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
free(valueName);
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
int _getAndSendDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
if (num==0)
return _getAndSendDataPointsNoNum(aqh, ep, value, tsBegin, tsEnd, refMsgId);
else if (num==1) {
_getAndSendLastDatapoint(aqh, ep, value, refMsgId);
return AQH_MSG_IPC_SUCCESS;
}
else
return _getAndSendDataPointsWithNum(aqh, ep, value, num, refMsgId);
}
int _getAndSendDataPointsNoNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetDataPoints(aqh->storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
if (tablePtr) {
_sendDataPointsResponse(aqh, ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSG_IPC_ERROR_NODATA;
}
}
int _getAndSendDataPointsWithNum(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetLastNDataPoints(aqh->storage, valueId, num);
if (tablePtr) {
_sendDataPointsResponse(aqh, ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSG_IPC_ERROR_NODATA;
}
}
void _sendDataPointsResponse(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId)
{
int numTableEntries;
int numDataPoints;
GWEN_MSG *outMsg;
numTableEntries=(int)(tablePtr[0]);
numDataPoints=numTableEntries/2;
outMsg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
value, &(tablePtr[1]), numDataPoints);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
void _getAndSendLastDatapoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE *value, uint32_t refMsgId)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
int rv;
uint64_t timestamp=0;
double data=0.0;
rv=AQH_Storage_GetLastDataPoint(aqh->storage, AQH_Value_GetId(value), &timestamp, &data);
if (rv<0) {
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSG_IPC_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSG_IPC_ERROR_NODATA; break;
default: resultCode=AQH_MSG_IPC_ERROR_GENERIC; break;
}
}
else {
outMsg=AQH_MultiDataDataIpcMsg_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
value, timestamp, data);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
return;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETDATAPOINTS_H
#define AQHOME_DATA_C_GETDATAPOINTS_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -1,106 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_getdevices.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_devices.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_DEVICESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendDeviceList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetDevices(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
const AQH_DEVICE_LIST *origDeviceList;
DBG_INFO(NULL, "HandleGetDevices");
origDeviceList=AQH_Storage_GetDeviceList(aqh->storage);
if (origDeviceList) {
DBG_INFO(NULL, "Have a list of %d devices", AQH_Device_List_GetCount(origDeviceList));
if (AQH_Device_List_GetCount(origDeviceList)<AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending all entries in one message");
_sendDeviceList(aqh, ep, origDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
else {
AQH_DEVICE_LIST *tmpDeviceList;
const AQH_DEVICE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpDeviceList=AQH_Device_List_new();
v=AQH_Device_List_First(origDeviceList);
while(v) {
const AQH_DEVICE *next;
AQH_DEVICE *copyOfDevice;
next=AQH_Device_List_Next(v);
copyOfDevice=AQH_Device_dup(v);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(aqh, ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
AQH_Device_List_Clear(tmpDeviceList);
}
v=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(aqh, ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg)); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
}
else {
/* empty list */
_sendDeviceList(aqh, ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
}
void _sendDeviceList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
GWEN_MSG *msg;
msg=AQH_DevicesDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
flags, vl);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETDEVICES_H
#define AQHOME_DATA_C_GETDEVICES_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetDevices(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
#endif

View File

@@ -1,107 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_getlastdatapoint.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_getdata.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_READDATA) {
char *valueName;
AQH_GetDataDataIpcMsg_Parse(recvdMsg, 0);
valueName=AQH_Tag16IpcMsg_GetTagDataAsNewString(recvdMsg, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL);
if (valueName && *valueName) {
const AQH_VALUE *storedValue;
storedValue=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName);
if (storedValue) {
uint64_t timestamp=0;
double data=0.0;
int rv;
rv=AQH_Storage_GetLastDataPoint(aqh->storage, AQH_Value_GetId(storedValue), &timestamp, &data);
if (rv<0) {
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSG_IPC_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSG_IPC_ERROR_NODATA; break;
default: resultCode=AQH_MSG_IPC_ERROR_GENERIC; break;
}
}
else {
outMsg=AQH_MultiDataDataIpcMsg_newForOne(AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
storedValue, timestamp, data);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
free(valueName);
return;
}
}
else {
DBG_INFO(NULL, "Value \"%s\" not found", valueName);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
free(valueName);
}
else {
DBG_INFO(NULL, "No name for value");
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETLASTDATAPOINT_H
#define AQHOME_DATA_C_GETLASTDATAPOINT_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg);
#endif

View File

@@ -1,106 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_getvalues.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_values.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_VALUESPERMSG 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _sendValueList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg)
{
const AQH_VALUE_LIST *origValueList;
DBG_INFO(NULL, "HandleGetValues");
origValueList=AQH_Storage_GetValueList(aqh->storage);
if (origValueList) {
DBG_INFO(NULL, "Have a list of %d values", AQH_Value_List_GetCount(origValueList));
if (AQH_Value_List_GetCount(origValueList)<AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending all entries in one message");
_sendValueList(aqh, ep, origValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
else {
AQH_VALUE_LIST *tmpValueList;
const AQH_VALUE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(origValueList);
while(v) {
const AQH_VALUE *next;
AQH_VALUE *copyOfValue;
next=AQH_Value_List_Next(v);
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
if (AQH_Value_List_GetCount(tmpValueList)>=AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(aqh, ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
AQH_Value_List_Clear(tmpValueList);
}
v=next;
}
if (AQH_Value_List_GetCount(tmpValueList)) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(aqh, ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg)); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
}
else {
/* empty list */
_sendValueList(aqh, ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, GWEN_IpcMsg_GetMsgId(msg));
}
}
void _sendValueList(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
GWEN_MSG *msg;
msg=AQH_ValuesDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP,
GWEN_MsgEndpoint_GetNextMessageId(ep), refMsgId,
flags, vl);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_GETVALUES_H
#define AQHOME_DATA_C_GETVALUES_H
#include "./aqhome_data.h"
void AqHomeData_HandleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg);
#endif

View File

@@ -1,114 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_getlastdatapoint.h"
#include "./aqhome_data_p.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_devices.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleModDevice(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_MODDEVICE) {
AQH_DEVICE *device;
AQH_DevicesDataIpcMsg_Parse(recvdMsg, 0);
device=AQH_DevicesDataIpcMsg_ReadFirstDevice(recvdMsg);
if (device) {
const char *deviceNameForSystem;
deviceNameForSystem=AQH_Device_GetNameForSystem(device);
if (deviceNameForSystem && *deviceNameForSystem) {
AQH_DEVICE *storedDevice;
storedDevice=AQH_Storage_GetDeviceByNameForSystem(aqh->storage, deviceNameForSystem);
if (storedDevice) {
const char *s;
s=AQH_Device_GetNameForGui(device);
if (s && *s)
AQH_Device_SetNameForGui(storedDevice, s);
s=AQH_Device_GetRoomName(device);
if (s && *s)
AQH_Device_SetRoomName(storedDevice, s);
s=AQH_Device_GetLocation(device);
if (s && *s)
AQH_Device_SetLocation(storedDevice, s);
s=AQH_Device_GetDescription(device);
if (s && *s)
AQH_Device_SetDescription(storedDevice, s);
AQH_Storage_AddRuntimeFlags(aqh->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
resultCode=AQH_MSG_IPC_SUCCESS;
}
else {
DBG_INFO(NULL, "Device \"%s\" not found", deviceNameForSystem);
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No name for value");
resultCode=AQH_MSG_IPC_ERROR_NOTFOUND;
}
}
else {
DBG_INFO(NULL, "No device info in message");
resultCode=AQH_MSG_IPC_ERROR_INVALID;
}
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data");
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_MODDEVICE_H
#define AQHOME_DATA_C_MODDEVICE_H
#include "./aqhome_data.h"
void AqHomeData_HandleModDevice(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

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

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_SETDATA_H
#define AQHOME_DATA_C_SETDATA_H
#include "./aqhome_data.h"
void AqHomeData_HandleSetData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

@@ -1,153 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./c_updatedata.h"
#include "./aqhome_data_p.h"
#include "./loop.h"
#include "aqhome/ipc/data/ipc_data.h"
#include "aqhome/ipc/data/msg_data_multidata.h"
#include "aqhome/ipc/endpoint_ipc.h"
#include "aqhome/ipc/msg_ipc_result.h"
#include "aqhome/ipc/msg_ipc_tag16.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
#define DISABLE_DEBUGLOG
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _storeDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues);
static void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
const uint64_t *dataPoints, uint32_t numValues);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_HandleUpdateData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg)
{
GWEN_MSG *outMsg;
int resultCode=AQH_MSG_IPC_SUCCESS;
const GWEN_TAG16 *tag;
AQH_VALUE *recvdValue;
const char *valueName;
const uint64_t *dataPoints=NULL;
unsigned int numberOfPoints=0;
AQH_MultiDataDataIpcMsg_Parse(recvdMsg, 0);
recvdValue=AQH_MultiDataDataIpcMsg_ReadValue(recvdMsg);
valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL;
tag=AQH_Tag16IpcMsg_FindFirstTagByType(recvdMsg, AQH_MSGDATA_MULTIDATA_TAGS_DATA);
dataPoints=tag?((const uint64_t*)GWEN_Tag16_GetTagData(tag)):NULL;
numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t));
if (numberOfPoints>0) {
AQH_VALUE *value;
value=AqHomeData_GetOrCreateValueForDriverWithTemplate(aqh, ep, recvdValue);
if (value) {
resultCode=_storeDataPoints(aqh, value, dataPoints, numberOfPoints);
if (resultCode==AQH_MSG_IPC_SUCCESS)
_sendDataChangedMsgToAllClients(aqh, ep, value, dataPoints, numberOfPoints);
}
else {
DBG_INFO(NULL, "No permissions to add datapoint for value \"%s\"", valueName);
resultCode=AQH_MSG_IPC_ERROR_PERMS;
}
}
else {
DBG_INFO(NULL, "No datapoints");
resultCode=AQH_MSG_IPC_ERROR_INVALID;
}
AQH_Value_free(recvdValue);
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT,
GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(recvdMsg),
resultCode);
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
}
int _storeDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues)
{
uint32_t i;
for(i=0; i<numValues; i++) {
uint64_t timestamp;
union {double f; uint64_t i;} u;
int rv;
timestamp=*(dataPoints++);
u.i=*(dataPoints++);
rv=AQH_Storage_AddDatapoint(aqh->storage, AQH_Value_GetId(v), timestamp, u.f);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return AQH_MSG_IPC_ERROR_GENERIC;
}
else {
DBG_DEBUG(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
}
} /* for */
return AQH_MSG_IPC_SUCCESS;
}
void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
const uint64_t *dataPoints, uint32_t numValues)
{
GWEN_MSG_ENDPOINT *ep;
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
while(ep) {
if (ep!=epSrc) {
if (GWEN_MsgEndpoint_GetFlags(ep) & AQH_IPCENDPOINT_FLAGS_WANTUPDATES) {
GWEN_MSG *msg;
DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint %s", GWEN_MsgEndpoint_GetName(ep));
msg=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
GWEN_MsgEndpoint_GetNextMessageId(ep), 0,
v, dataPoints, numValues);
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s doesn't want updates", GWEN_MsgEndpoint_GetName(ep));
}
}
else {
DBG_DEBUG(AQH_LOGDOMAIN, "Not sending update msg to source of updates");
}
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
}
}

View File

@@ -1,25 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_C_UPDATEDATA_H
#define AQHOME_DATA_C_UPDATEDATA_H
#include "./aqhome_data.h"
void AqHomeData_HandleUpdateData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg);
#endif

View File

@@ -1,80 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./fini.h"
#include "./aqhome_data_p.h"
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/args.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endpoint_tcpd.h>
#include <unistd.h>
/* ------------------------------------------------------------------------------------------------
* defines
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void _disconnectTree(GWEN_MSG_ENDPOINT *ep);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AqHomeData_Fini(AQHOME_DATA *aqh)
{
if (aqh) {
if (aqh->ipcdEndpoint) {
_disconnectTree(aqh->ipcdEndpoint);
GWEN_MsgEndpoint_Disconnect(aqh->ipcdEndpoint);
}
GWEN_MsgEndpoint_free(aqh->ipcdEndpoint);
aqh->ipcdEndpoint=NULL;
if (aqh->pidFile)
remove(aqh->pidFile);
}
}
void _disconnectTree(GWEN_MSG_ENDPOINT *ep)
{
GWEN_MSG_ENDPOINT *epChild;
epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep);
while(epChild) {
_disconnectTree(epChild);
epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild);
} /* while */
GWEN_MsgEndpoint_Disconnect(ep);
}

View File

@@ -1,23 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_FINI_H
#define AQHOME_DATA_FINI_H
#include "./aqhome_data.h"
void AqHomeData_Fini(AQHOME_DATA *aqh);
#endif

View File

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

View File

@@ -1,23 +0,0 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_INIT_H
#define AQHOME_DATA_INIT_H
#include "./aqhome_data.h"
int AqHomeData_Init(AQHOME_DATA *aqh, int argc, char **argv);
#endif

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