183 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
360 changed files with 16637 additions and 10809 deletions

3
0BUILD
View File

@@ -2,7 +2,7 @@
<gwbuild>
<project name="aqhome" version="0.0.12" so_current="0" so_age="0" so_revision="12" 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)
@@ -153,6 +153,7 @@
<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 @@
<includes type="c" >
$(gwenhywfar_cflags)
$(aqcgi_cflags)
$(aqdiagram_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
@@ -21,6 +22,8 @@
--include=$(srcdir)
</includes>
<define name="AQHOME_CGI_WWWDIR" value="$(httpdatadir)/aqhome-cgi" quoted="TRUE" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
@@ -47,15 +50,16 @@
</sources>
<useTargets>
aqhome
aqhomecgi
aqhcgi_service
aqhome
</useTargets>
<libraries>
$(gwenhywfar_libs)
-lm
$(aqcgi_libs)
$(aqdiagram_libs)
</libraries>
<subdirs>
@@ -125,6 +129,7 @@
$(gwenhywfar_libs)
-lm
$(aqcgi_libs)
$(aqdiagram_libs)
</libraries>
<subdirs>

View File

@@ -2,10 +2,15 @@
#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>
@@ -20,22 +25,48 @@
#define AQHOME_CGI_LOGFILE "/var/www/aqhome-cgi/log/aqhome-cgi.log"
#define AQHOME_CGI_DEFAULT_STATIC_FILES "0-build/services/static"
#define AQHOME_CGI_DEFAULT_RUNTIME_FILES "0-build/services/runtime"
#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"
void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles);
int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles);
AQH_MODULE *_loadModule(AQH_SERVICE *sv, AQCGI_REQUEST *rq, AQH_MODULE *mParent, const char *sModuleName);
/* ------------------------------------------------------------------------------------------------
* 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;
AQCGI_REQUEST *rq;
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();
@@ -45,59 +76,64 @@ int main(int argc, char **argv)
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);
DBG_ERROR(NULL, "Init CGI");
AQCGI_Init();
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);
rq=AQCGI_ReadRequest();
if (rq) {
const char *sPathStaticFiles;
const char *sPathRuntimeFiles;
if (argc>1 && argv[1]) {
if (0==strcasecmp(argv[1], "init")) {
int rv;
sPathStaticFiles=getenv("AQHOME_STATIC_FILES");
if (!(sPathStaticFiles && *sPathStaticFiles))
sPathStaticFiles=AQHOME_CGI_DEFAULT_STATIC_FILES;
rv=_init(sPathRuntimeFiles, sBaseUrl);
if (rv<0) {
fprintf(stderr, "Error on init (%d)\n", rv);
return 2;
}
sPathRuntimeFiles=getenv("AQHOME_RUNTIME_FILES");
if (!(sPathRuntimeFiles && *sPathRuntimeFiles))
sPathRuntimeFiles=AQHOME_CGI_DEFAULT_RUNTIME_FILES;
_handleRequest(rq, sPathStaticFiles, sPathRuntimeFiles);
}
}
else {
fprintf(stdout, "Content-type: text/plain\n\n");
fprintf(stdout, "Error: No Request!\n");
return 0;
}
AQCGI_REQUEST *rq;
AQCGI_Fini();
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)
void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles, const char *sBaseUrl)
{
AQH_SERVICE *sv;
int rv;
sv=AQH_ServiceFiles_new(sPathRuntimeFiles);
sv=AQH_ServiceFiles_new(sPathRuntimeFiles, sBaseUrl);
rv=_handlePath(sv, rq, sPathStaticFiles);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
AQH_Service_free(sv);
}
@@ -108,10 +144,13 @@ 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) {
@@ -129,10 +168,10 @@ int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles
AQH_MODULE *m;
DBG_ERROR(NULL, "Entry: %s (%s)", s, seNext?"not last":"last");
m=AQH_ModService_LoadSubModule(mParent, rq, s);
if (m==NULL) {
m=AQH_ModService_LoadSubModule(mParent, rq, session, s);
if (m==NULL) {
AQH_Session_free(session);
AQH_Module_free(mRoot);
AQCGI_SendResponseWithStatus(rq, 404, "Not found");
return GWEN_ERROR_GENERIC;
}
mParent=m;
@@ -141,9 +180,11 @@ int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles
int rv;
/* last, let module handle remaining part */
rv=AQH_ModService_HandleRequest(mParent, rq, s);
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;
}
@@ -153,10 +194,12 @@ int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles
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;
}
@@ -177,5 +220,45 @@ void logStart()
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

@@ -20,7 +20,7 @@
</includes>
<define name="BUILDING_AQHOME" />
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
@@ -49,13 +49,12 @@
<headers dist="true" install="$(pkgincludedir)/service" >
mservice.h
mdataclient.h
mroot.h
</headers>
<headers dist="true" >
mservice_p.h
mroot_p.h
</headers>
@@ -63,7 +62,7 @@
<sources>
$(local/typefiles)
mservice.c
mdataclient.c
mroot.c
</sources>
@@ -73,9 +72,13 @@
<useTargets>
aqhcgi_modcom
aqhcgi_mdevices
</useTargets>
<subdirs>
common
devices
static
</subdirs>

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

@@ -9,16 +9,19 @@
#ifndef AQHOME_CGI_MSERVICE_P_H
#define AQHOME_CGI_MSERVICE_P_H
#include "aqhome-cgi/modules/mservice.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;
};

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

@@ -14,6 +14,8 @@
#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>
@@ -35,11 +37,12 @@
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
static int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq);
static int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq);
static AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq);
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);
/* ------------------------------------------------------------------------------------------------
@@ -51,7 +54,8 @@ AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MODULE *m;
m=AQH_ModService_new(sv, baseFolder);
m=AQH_Module_new();
AQH_ModService_Extend(m, sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
@@ -60,69 +64,132 @@ AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder)
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName)
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, const char *sLastPathElem)
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)
return _handleRqLogin(m, rq);
_handleRqLogin(m, rq, dbuf);
else if (strcasecmp(sLastPathElem, "signup")==0) {
AQCGI_SendResponseWithStatus(rq, 501, "Not Implemented");
return GWEN_ERROR_NOT_IMPLEMENTED;
AQCGI_Request_SetResponseCode(rq, 501);
AQCGI_Request_SetResponseText(rq, "Not Implemented");
}
else if (strcasecmp(sLastPathElem, "confirm")==0) {
AQCGI_SendResponseWithStatus(rq, 501, "Not Implemented");
return GWEN_ERROR_NOT_IMPLEMENTED;
AQCGI_Request_SetResponseCode(rq, 501);
AQCGI_Request_SetResponseText(rq, "Not Implemented");
}
else if (strcasecmp(sLastPathElem, "index.html")==0)
_handleRqIndex(m, rq, dbuf);
else {
AQCGI_SendResponseWithStatus(rq, 404, "Not Found");
return GWEN_ERROR_NOT_IMPLEMENTED;
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)
int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
int rv;
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
rv=AQH_ModService_RespondWithFile(m, rq, "en", "login.html");
AQH_ModService_RespondWithFile(m, rq, "en", "login.html", dbuf);
else if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_POST)
rv=_handleRqLoginPost(m, rq);
_handleRqLoginPost(m, rq, dbuf);
else {
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
AQCGI_SendResponseWithStatus(rq, 405, "Method No Allowed");
return GWEN_ERROR_INVALID;
}
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
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)
int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
AQH_SERVICE *sv;
AQH_USER *user;
AQH_SESSION *session;
GWEN_BUFFER *dbuf;
GWEN_BUFFER *tbuf;
GWEN_TIMESTAMP *ts;
int rv;
DBG_ERROR(NULL, "Handling request");
sv=AQH_ModService_GetService(m);
user=_getAndCheckUser(m, rq);
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;
}
@@ -132,37 +199,42 @@ int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq)
rv=AQH_Service_SaveUser(sv, user);
if (rv<0) {
DBG_ERROR(NULL, "Error saving user \"%s\"", AQH_User_GetAlias(user));
AQCGI_SendResponseWithStatus(rq, 500, "Internal Error");
AQCGI_Request_SetResponseCode(rq, 500);
AQCGI_Request_SetResponseText(rq, "Internal Error");
AQH_User_free(user);
return rv;
}
/* generate session */
DBG_ERROR(NULL, "Generating session");
dbuf=GWEN_Buffer_new(0, 64, 0, 1);
AQCGI_GenerateSessionId(dbuf);
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(dbuf));
GWEN_Buffer_free(dbuf);
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\"", AQH_User_GetAlias(user));
AQCGI_SendResponseWithStatus(rq, 500, "Internal Error");
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 */
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "Set-Cookie: session=%s; max-age=3600", AQH_Session_GetUid(session));
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(dbuf));
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_SendResponseWithStatus(rq, 200, "Ok");
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);
@@ -171,7 +243,7 @@ int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq)
AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq, GWEN_BUFFER *dbuf)
{
GWEN_DB_NODE *dbPost;
@@ -189,7 +261,8 @@ AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
sPasswd=GWEN_DB_GetCharValue(dbPost, "password", 0, NULL);
if (!(sUserName && *sUserName && sPasswd && *sPasswd)) {
DBG_ERROR(NULL, "Either user name or password missing");
AQCGI_SendResponseWithStatus(rq, 400, "Bad Request");
AQCGI_Request_SetResponseCode(rq, 400);
AQCGI_Request_SetResponseText(rq, "Bad Request");
return NULL;
}
@@ -197,14 +270,16 @@ AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
user=AQH_Service_LoadUser(sv, sUserName);
if (user==NULL) {
DBG_ERROR(NULL, "User \"%s\" not found", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
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_SendResponseWithStatus(rq, 403, "Forbidden");
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
AQH_User_free(user);
return NULL;
}
@@ -212,16 +287,19 @@ AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
hashedPaswd=AQH_User_GetHashedPassword(user);
if (!(hashedPaswd && *hashedPaswd)) {
DBG_ERROR(NULL, "User \"%s\" has no hashed password", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
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_SendResponseWithStatus(rq, 403, "Forbidden");
AQCGI_Request_SetResponseCode(rq, 403);
AQCGI_Request_SetResponseText(rq, "Forbidden");
GWEN_Buffer_free(buf);
AQH_User_free(user);
return NULL;
@@ -234,6 +312,8 @@ AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
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

@@ -9,7 +9,7 @@
#ifndef AQHOME_CGI_MROOT_H
#define AQHOME_CGI_MROOT_H
#include <aqhome-cgi/modules/mservice.h>
#include <aqhome-cgi/modules/common/mservice.h>
#include <aqcgi/request.h>

View File

@@ -1,291 +0,0 @@
/****************************************************************************
* 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"
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
GWEN_INHERIT(AQH_MODULE, AQH_MOD_SERVICE)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
AQH_MODULE *AQH_ModService_new(AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MODULE *m;
AQH_MOD_SERVICE *xm;
m=AQH_Module_new();
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;
return m;
}
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;
}
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;
}
}
}
int AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MODULE *mParent;
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent) {
int rv;
rv=AQH_ModService_AddHeader(mParent, lang, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
}
return AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_HEADERFILE, dbuf);
}
DBG_ERROR(NULL, "Argument missing");
return GWEN_ERROR_INVALID;
}
int AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MODULE *mParent;
int rv;
rv=AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_FOOTERFILE, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent) {
int rv;
rv=AQH_ModService_AddFooter(mParent, lang, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
}
return 0;
}
else {
DBG_ERROR(NULL, "Argument missing");
return GWEN_ERROR_INVALID;
}
}
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename)
{
GWEN_BUFFER *buf;
int rv;
buf=GWEN_Buffer_new(0, 256, 0, 1);
rv=AQH_ModService_AddHeader(m, lang, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
rv=AQH_ModService_ReadStaticFile(m, lang, sFilename, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
rv=AQH_ModService_AddFooter(m, lang, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
AQCGI_Request_SetBufferResponseBody(rq, buf);
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
AQCGI_SendResponseWithStatus(rq, 200, "Ok");
return 0;
}
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, 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, sLastPathElem);
}
return GWEN_ERROR_NOT_IMPLEMENTED;
}
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm && xm->loadSubModuleFn)
return xm->loadSubModuleFn(m, rq, sModuleName);
}
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;
}

View File

@@ -1,46 +0,0 @@
/****************************************************************************
* 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 <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
typedef int (*AQH_MODSERVICE_HANDLEREQUEST_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
typedef AQH_MODULE* (*AQH_MODSERVICE_LOADSUBMODULE_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
AQH_MODULE *AQH_ModService_new(AQH_SERVICE *sv, const char *baseFolder);
AQH_SERVICE *AQH_ModService_GetService(const AQH_MODULE *m);
const char *AQH_ModService_GetBaseFolder(const AQH_MODULE *m);
int AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
int AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename);
int AQH_ModService_ReadStaticFile(AQH_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf);
void AQH_ModService_SetHandleRequestFn(AQH_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn);
void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn);
#endif

View File

@@ -6,6 +6,7 @@
header.html
footer.html
login.html
index.html
</data>

View File

@@ -10,8 +10,17 @@
<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,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

@@ -20,7 +20,7 @@
</includes>
<define name="BUILDING_AQHOME" />
<define name="not_BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>

View File

@@ -46,7 +46,7 @@
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember</flags>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="flags" type="uint32_t" maxlen="4">
@@ -60,7 +60,7 @@
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own with_getbymember</flags>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="descr" type="char_ptr" maxlen="256">

View File

@@ -31,21 +31,21 @@
<members>
<member name="moduleId" type="uint32_t" maxlen="4">
<member name="moduleId" type="char_ptr" maxlen="256">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember</flags>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="exclAddPerms" type="uint32_t" maxlen="4">
<member name="explAddPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
</member>
<member name="exclDelPerms" type="uint32_t" maxlen="4">
<member name="explDelPerms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
@@ -59,14 +59,6 @@
<flags></flags>
</member>
<member name="perms" type="uint32_t" maxlen="4">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>volatile</flags>
</member>
</members>
</type>

View File

@@ -43,6 +43,13 @@
<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>

View File

@@ -27,7 +27,7 @@ GWEN_LIST_FUNCTIONS(AQH_SERVICE, AQH_Service);
AQH_SERVICE *AQH_Service_new(void)
AQH_SERVICE *AQH_Service_new(const char *baseFolder, const char *baseUrl)
{
AQH_SERVICE *sv;
@@ -35,6 +35,32 @@ AQH_SERVICE *AQH_Service_new(void)
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;
}
@@ -46,11 +72,44 @@ void AQH_Service_free(AQH_SERVICE *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)
{

View File

@@ -52,10 +52,13 @@ typedef GWEN_STRINGLIST* (*AQH_SERVICE_LISTSESSIONS_FN)(AQH_SERVICE *sv);
AQH_SERVICE *AQH_Service_new(void);
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);

View File

@@ -17,6 +17,11 @@ 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;

View File

@@ -32,6 +32,10 @@
</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" />
@@ -69,7 +73,7 @@
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>with_getbymember</flags>
<flags>with_getbymember sortbymember</flags>
</member>
<member name="flags" type="uint32_t" maxlen="4">
@@ -83,21 +87,21 @@
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags></flags>
<flags>sortbymember</flags>
</member>
<member name="name" type="char_ptr" maxlen="16">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
<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</flags>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="hashedPassword" type="char_ptr" maxlen="128">
@@ -111,7 +115,7 @@
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
<flags>own with_getbymember sortbymember</flags>
</member>
<member name="notes" type="char_ptr" maxlen="256">
@@ -125,14 +129,14 @@
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<flags>own sortbymember</flags>
</member>
<member name="timestampLastLogin" type="gwen_timestamp" maxlen="8">
<default>NULL</default>
<preset>NULL</preset>
<access>public</access>
<flags>own</flags>
<flags>own sortbymember</flags>
</member>
<member name="modulePermList" type="AQH_MODULE_PERMS_LIST">

View File

@@ -79,16 +79,15 @@ static GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName);
* ------------------------------------------------------------------------------------------------
*/
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder)
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();
sv=AQH_Service_new(baseFolder, baseUrl);
GWEN_NEW_OBJECT(AQH_SERVICE_FILE, xs);
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv, xs, _freeData);
xs->baseFolder=strdup(baseFolder);
AQH_Service_SetLoadUserFn(sv, _loadUser);
AQH_Service_SetSaveUserFn(sv, _saveUser);
@@ -109,7 +108,7 @@ AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder)
AQH_Service_SetListSessionsFn(sv, _listSessions);
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "dir://%s", baseFolder);
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) {
@@ -131,7 +130,6 @@ void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
xs=(AQH_SERVICE_FILE*) p;
GWEN_ConfigMgr_free(xs->configMgr);
free(xs->baseFolder);
GWEN_FREE_OBJECT(xs);
}
@@ -213,6 +211,9 @@ int _addUser(AQH_SERVICE *sv, AQH_USER *user)
GWEN_DB_Group_free(db);
return 0;
}
else {
DBG_ERROR(NULL, "Missing alias");
}
return GWEN_ERROR_INVALID;
}

View File

@@ -13,7 +13,7 @@
#include "aqhome-cgi/service/service.h"
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder);
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder, const char *baseUrl);

View File

@@ -1,6 +1,6 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
@@ -18,7 +18,6 @@
typedef struct AQH_SERVICE_FILE AQH_SERVICE_FILE;
struct AQH_SERVICE_FILE {
char *baseFolder;
GWEN_CONFIGMGR *configMgr;
};

View File

@@ -138,10 +138,8 @@ int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep,
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
if (num==0 || num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES;
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, num);
if (tablePtr) {
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
@@ -163,8 +161,6 @@ int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep,
uint64_t valueId;
uint64_t *tablePtr;
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetLastNDataPoints(storage, valueId, num);
if (tablePtr) {
@@ -187,8 +183,6 @@ int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep,
uint64_t valueId;
uint64_t *tablePtr;
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetFirstNDataPoints(storage, valueId, num);
if (tablePtr) {
@@ -210,14 +204,28 @@ void _sendDataPointsResponse(AQH_OBJECT *ep,
{
int numTableEntries;
int numDataPoints;
AQH_MESSAGE *outMsg;
numTableEntries=(int)(tablePtr[0]);
numDataPoints=numTableEntries/2;
outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
value, &(tablePtr[1]), numDataPoints);
AQH_Endpoint_AddMsgOut(ep, outMsg);
tablePtr++;
while(numDataPoints) {
AQH_MESSAGE *outMsg;
int toSend;
uint32_t flags=0;
toSend=numDataPoints;
if (toSend>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
toSend=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
numDataPoints-=toSend;
if (numDataPoints==0)
flags|=AQH_MSGDATA_MULTIDATA_FLAGS_LASTMSG;
outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId, flags,
value, tablePtr, toSend);
tablePtr+=(toSend*2);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

View File

@@ -16,11 +16,13 @@
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdevices.h"
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
@@ -38,7 +40,10 @@
* ------------------------------------------------------------------------------------------------
*/
static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
static AQH_DEVICE_LIST *_getMatchingDeviceList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList);
static int _deviceMatches(const AQH_DEVICE *dev, const char *deviceName);
static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t refMsgId);
static void _sendDeviceListMsg(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId);
@@ -47,63 +52,114 @@ static void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, GWEN_UNUSED const GWEN_TAG16_LIST *tagList)
void AqHomeDataServer_HandleGetDevices(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
const AQH_DEVICE_LIST *origDeviceList;
AQH_DEVICE_LIST *deviceList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_INFO(NULL, "HandleGetDevices");
origDeviceList=AQH_Storage_GetDeviceList(xo->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(ep, origDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
}
else {
AQH_DEVICE_LIST *tmpDeviceList;
const AQH_DEVICE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpDeviceList=AQH_Device_List_new();
v=AQH_Device_List_First(origDeviceList);
while(v) {
const AQH_DEVICE *next;
AQH_DEVICE *copyOfDevice;
next=AQH_Device_List_Next(v);
copyOfDevice=AQH_Device_dup(v);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
AQH_Device_List_Clear(tmpDeviceList);
}
v=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceList(ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
deviceList=_getMatchingDeviceList(xo, tagList);
if (deviceList) {
_sendDeviceList(ep, deviceList, refMsgId);
AQH_Device_List_free(deviceList);
}
else {
/* empty list */
_sendDeviceList(ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
_sendDeviceListMsg(ep, NULL, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
}
}
}
void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
AQH_DEVICE_LIST *_getMatchingDeviceList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList)
{
const AQH_DEVICE_LIST *origDeviceList;
AQH_DEVICE_LIST *tmpDeviceList=NULL;
char *deviceName;
deviceName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDEVICES_TAGS_DEVICENAME, NULL):NULL;
origDeviceList=AQH_Storage_GetDeviceList(xo->storage);
if (origDeviceList) {
const AQH_DEVICE *dev;
tmpDeviceList=AQH_Device_List_new();
dev=AQH_Device_List_First(origDeviceList);
while(dev) {
if (_deviceMatches(dev, deviceName)) {
AQH_DEVICE *copyOfDevice;
copyOfDevice=AQH_Device_dup(dev);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
}
dev=AQH_Device_List_Next(dev);
}
if (AQH_Device_List_GetCount(tmpDeviceList)<1) {
AQH_Device_List_free(tmpDeviceList);
tmpDeviceList=NULL;
}
}
free(deviceName);
return tmpDeviceList;
}
int _deviceMatches(const AQH_DEVICE *dev, const char *deviceName)
{
if (deviceName && *deviceName) {
const char *s;
s=AQH_Device_GetNameForSystem(dev);
if (s && *s && GWEN_Text_ComparePattern(s, deviceName, 0)==-1)
return 0;
}
return 1;
}
void _sendDeviceList(AQH_OBJECT *ep, const AQH_DEVICE_LIST *deviceList, uint32_t refMsgId)
{
AQH_DEVICE_LIST *tmpDeviceList;
const AQH_DEVICE *dev;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpDeviceList=AQH_Device_List_new();
dev=AQH_Device_List_First(deviceList);
while(dev) {
const AQH_DEVICE *next;
AQH_DEVICE *copyOfDevice;
next=AQH_Device_List_Next(dev);
copyOfDevice=AQH_Device_dup(dev);
AQH_Device_List_Add(copyOfDevice, tmpDeviceList);
if (AQH_Device_List_GetCount(tmpDeviceList)>=AQHOMEDATA_DEVICESPERMSG) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceListMsg(ep, tmpDeviceList, next?0:AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId);
AQH_Device_List_Clear(tmpDeviceList);
}
dev=next;
}
if (AQH_Device_List_GetCount(tmpDeviceList)) {
DBG_INFO(NULL, "Sending %d devices", AQH_Device_List_GetCount(tmpDeviceList));
_sendDeviceListMsg(ep, tmpDeviceList, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Device_List_free(tmpDeviceList);
}
void _sendDeviceListMsg(AQH_OBJECT *ep, const AQH_DEVICE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;

View File

@@ -42,14 +42,15 @@
* ------------------------------------------------------------------------------------------------
*/
static void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData);
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, const char *data);
const AQH_VALUE *v, double data);
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data);
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, double data);
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
@@ -76,11 +77,11 @@ void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_
recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList);
if (recvdValue) {
const char *valueName;
char *valueDataFreeable;
double valueData;
AQH_VALUE *systemValue;
valueName=AQH_Value_GetNameForSystem(recvdValue);
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
valueData=AQH_IpcdMessageSetData_ReadData(tagList);
systemValue=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
if (systemValue) {
@@ -96,8 +97,10 @@ void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_
AQH_MSG_REQUEST *rq;
DBG_ERROR(NULL, "Creating SETDATA request for driver endpoint (%s)", AQH_Endpoint_GetServiceName(epDriver));
rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueDataFreeable);
rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueData);
AqHomeDataServer_AddRequestToTree(o, rq);
_storeDatapoint(xo, systemValue, valueData);
}
else {
DBG_ERROR(NULL, "Driver \"%s\" not available", driverName);
@@ -119,7 +122,6 @@ void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_
AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND);
}
AQH_Value_free(recvdValue);
free(valueDataFreeable);
} /* if recvdValue */
else {
DBG_ERROR(NULL, "No value in message");
@@ -131,6 +133,23 @@ void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_
void _storeDatapoint(AQHOME_SERVER *xo, const AQH_VALUE *v, double valueData)
{
uint64_t timestamp;
int rv;
timestamp=(uint64_t) time(NULL);
rv=AQH_Storage_AddDatapoint(xo->storage, AQH_Value_GetId(v), timestamp, valueData);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
}
else {
DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
@@ -138,7 +157,7 @@ void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o,
AQH_OBJECT *epSrc, uint32_t requestMsgId,
AQH_OBJECT *epDriver,
const AQH_VALUE *v, const char *data)
const AQH_VALUE *v, double data)
{
AQH_MSG_REQUEST *rq;
AQH_MSG_REQUEST *subRq;
@@ -205,7 +224,7 @@ void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
*/
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data)
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, double data)
{
AQH_MSG_REQUEST *rq;
uint16_t msgId;

View File

@@ -152,7 +152,7 @@ void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc, const
DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint");
msg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
AQH_Endpoint_GetNextMessageId(ep), 0,
AQH_Endpoint_GetNextMessageId(ep), 0, 0,
v, dataPoints, numValues);
AQH_Endpoint_AddMsgOut(ep, msg);
}

View File

@@ -79,6 +79,8 @@ static int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
static int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
static void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep);
static void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg);
static void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName, const AQH_VALUE *valueTemplate);
static void _updateValue(AQHOME_SERVER *xo, AQH_VALUE *v, const char *deviceName, const AQH_VALUE *valueTemplate);
static AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName);
static int _createPidFile(const char *pidFilename);
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
@@ -652,32 +654,16 @@ AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o,
v=AQH_Storage_GetValueByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
if (v==NULL) {
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE) {
AQH_DEVICE *device;
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf));
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
v=AQH_Value_new();
AQH_Value_SetDriver(v, serviceName);
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
AQH_Value_SetNameForSystem(v, GWEN_Buffer_GetStart(buf));
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
AQH_Value_SetTimestampCreation(v, (uint64_t) time(NULL));
if (device) {
AQH_Value_SetDeviceNameForSystem(v, AQH_Device_GetNameForSystem(device));
AQH_Value_SetDeviceName(v, AQH_Device_GetName(device));
}
AQH_Storage_AddValue(xo->storage, v);
}
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE)
_createValue(xo, epDriver, GWEN_Buffer_GetStart(buf), valueTemplate);
else {
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
return NULL;
}
}
else
_updateValue(xo, v, GWEN_Buffer_GetStart(buf), valueTemplate);
GWEN_Buffer_free(buf);
return v;
}
@@ -686,6 +672,75 @@ AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o,
void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName, const AQH_VALUE *valueTemplate)
{
AQH_DEVICE *device;
const char *serviceName;
AQH_VALUE *v;
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", deviceName);
serviceName=AQH_Endpoint_GetServiceName(epDriver);
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
v=AQH_Value_new();
AQH_Value_SetDriver(v, serviceName);
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
AQH_Value_SetNameForSystem(v, deviceName);
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
AQH_Value_SetTimestampCreation(v, (uint64_t) time(NULL));
if (device) {
AQH_Value_SetDeviceNameForSystem(v, AQH_Device_GetNameForSystem(device));
AQH_Value_SetDeviceName(v, AQH_Device_GetName(device));
}
AQH_Storage_AddValue(xo->storage, v);
}
void _updateValue(AQHOME_SERVER *xo, AQH_VALUE *value, const char *deviceName, const AQH_VALUE *valueTemplate)
{
int chg=0;
const char *s1;
const char *s2;
int i1;
int i2;
DBG_INFO(AQH_LOGDOMAIN, "Updating value \"%s\"", deviceName);
s1=AQH_Value_GetValueUnits(valueTemplate);
s2=AQH_Value_GetValueUnits(value);
if (s1 && *s1) {
if (!(s2 && *s2) || (strcasecmp(s1, s2)!=0)) {
AQH_Value_SetValueUnits(value, s1);
chg=1;
}
}
i1=AQH_Value_GetValueType(valueTemplate);
i2=AQH_Value_GetValueType(value);
if (i1!=AQH_ValueDataType_Unknown) {
if (i1!=i2) {
AQH_Value_SetValueType(value, i1);
chg=1;
}
}
i1=AQH_Value_GetModality(valueTemplate);
i2=AQH_Value_GetModality(value);
if (i1!=AQH_ValueModality_Unknown) {
if (i1!=i2) {
AQH_Value_SetModality(value, i1);
chg=1;
}
}
if (chg)
AQH_Storage_AddRuntimeFlags(xo->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
}
AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName)
{
const char *serviceName;

View File

@@ -218,7 +218,7 @@ void _sendMessage(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device, const AQ
AQH_MESSAGE *pubMsg;
pubMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0, 0,
msgValue, now, f);
DBG_INFO(AQH_LOGDOMAIN, "BROKER UPDATE_DATA %s/%s: %f",
deviceName?deviceName:"<no device name>",

View File

@@ -30,8 +30,13 @@
*/
static void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo, const AQHMQTT_DEVICE *device,
const char *valueName, const char *valueData);
static void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData);
const char *valueName, double valueData);
static void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo,
const AQHMQTT_DEVICE *device,
const AQHMQTT_TOPIC *topic,
const AQHMQTT_VALUE *value,
double valueData);
static const char *_valueTranslatedForDriver(const AQHMQTT_VALUE *value, double valueData);
static GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic);
@@ -65,12 +70,11 @@ void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o,
device=AQH_MqttLogServer_FindRegisteredDevice(o, deviceName);
if (device) {
char *valueDataFreeable;
double valueData;
DBG_ERROR(NULL, "Sending data to value \"%s\" of device \"%s\"", valueName, deviceName);
valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList);
_sendDataForDevice(xo, device, valueName, valueDataFreeable);
free(valueDataFreeable);
valueData=AQH_IpcdMessageSetData_ReadData(tagList);
_sendDataForDevice(xo, device, valueName, valueData);
}
else {
DBG_ERROR(NULL, "Device \"%s\" not found", deviceName);
@@ -93,7 +97,7 @@ void AQH_MqttLogServer_HandleSetData(AQH_OBJECT *o,
void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo,
const AQHMQTT_DEVICE *device,
const char *valueName, const char *valueData)
const char *valueName, double valueData)
{
const char *deviceId;
@@ -116,7 +120,7 @@ void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo,
if (value) {
/* found value, create publish msg, send */
DBG_ERROR(NULL, "Topic \"%s\" contains value \"%s\"", AQHMQTT_Topic_GetName(topic), valueName);
_sendValueToMqtt(xo, deviceId, topic, valueData);
_sendValueToMqtt(xo, device, topic, value, valueData);
}
} /* if out */
topic=AQHMQTT_Topic_List_Next(topic);
@@ -130,32 +134,82 @@ void _sendDataForDevice(AQH_MQTTLOG_SERVER *xo,
void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo, const char *deviceId, const AQHMQTT_TOPIC *topic, const char *valueData)
void _sendValueToMqtt(AQH_MQTTLOG_SERVER *xo,
const AQHMQTT_DEVICE *device,
const AQHMQTT_TOPIC *topic,
const AQHMQTT_VALUE *value,
double valueData)
{
const char *deviceId;
const char *translatedValue;
GWEN_BUFFER *buf;
#if !DEBUG_DRY_RUN
AQH_MESSAGE *msgOut;
#endif
deviceId=AQHMQTT_Device_GetId(device);
buf=_createBufferForTopic(deviceId, topic);
translatedValue=_valueTranslatedForDriver(value, valueData);
if (translatedValue && *translatedValue) {
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), translatedValue);
msgOut=AQH_MqttMessagePublish_new(0, 0, GWEN_Buffer_GetStart(buf), (const uint8_t*)translatedValue, strlen(translatedValue));
}
else {
GWEN_BUFFER *vbuf;
vbuf=GWEN_Buffer_new(0, 64, 0, 1);
GWEN_Buffer_AppendArgs(vbuf, "%f", valueData);
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetStart(vbuf));
msgOut=AQH_MqttMessagePublish_new(0, 0,
GWEN_Buffer_GetStart(buf),
(const uint8_t*)GWEN_Buffer_GetStart(vbuf),
GWEN_Buffer_GetUsedBytes(vbuf));
GWEN_Buffer_free(vbuf);
}
#if !DEBUG_DRY_RUN
DBG_ERROR(NULL, "MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
msgOut=AQH_MqttMessagePublish_new(0, 0, GWEN_Buffer_GetStart(buf),
(const uint8_t*) (valueData?valueData:NULL),
valueData?strlen(valueData):0);
if (msgOut)
AQH_Endpoint_AddMsgOut(xo->mqttEndpoint, msgOut);
else {
DBG_ERROR(NULL, "Error creating message");
}
#else
DBG_ERROR(NULL, "Would MQTT PUBLISH: %s = %s", GWEN_Buffer_GetStart(buf), valueData?valueData:"<empty>");
#endif
GWEN_Buffer_free(buf);
}
const char *_valueTranslatedForDriver(const AQHMQTT_VALUE *value, double valueData)
{
const AQHMQTT_TRANSLATION_LIST *translationList;
translationList=AQHMQTT_Value_GetTranslationList(value);
if (translationList) {
const AQHMQTT_TRANSLATION *t;
int valueAsInt;
valueAsInt=(int) valueData;
t=AQHMQTT_Translation_List_GetByAqhValue(translationList, valueAsInt);
if (t) {
const char *s;
s=AQHMQTT_Translation_GetDriverValue(t);
DBG_ERROR(NULL, "Translated value %d to %s", valueAsInt, s);
return s;
}
else {
DBG_ERROR(NULL, "No translation found for %d", valueAsInt);
}
}
else {
DBG_ERROR(NULL, "No translation list");
}
return NULL;
}
GWEN_BUFFER *_createBufferForTopic(const char *deviceId, const AQHMQTT_TOPIC *topic)
{
GWEN_BUFFER *buf;

View File

@@ -32,18 +32,18 @@
<members>
<member name="aqhValue" type="char_ptr" maxlen="128">
<member name="aqhValue" type="int" maxlen="8">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
<flags>with_getByMember</flags>
</member>
<member name="driverValue" type="char_ptr" maxlen="128">
<default>0</default>
<preset>0</preset>
<access>public</access>
<flags>own</flags>
<flags>own with_getByMember</flags>
</member>

View File

@@ -62,6 +62,7 @@ static AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode);
AQHMQTT_DEVICE_LIST *AQH_MqttLogServer_ReadDeviceFile(AQH_OBJECT *o, const char *sFilename)
{
DBG_ERROR(NULL, "Reading device file \"%s\"", sFilename);
if (o) {
AQH_MQTTLOG_SERVER *xo;
@@ -390,13 +391,15 @@ AQHMQTT_VALUE_LIST *_readXmlValueList(GWEN_XMLNODE *parentNode)
AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
{
const char *sValueName;
AQHMQTT_VALUE *value;
GWEN_XMLNODE *translationNode;
const char *s;
int i;
sValueName=GWEN_XMLNode_GetProperty(valueNode, "name", NULL);
value=AQHMQTT_Value_new();
AQHMQTT_Value_SetName(value, GWEN_XMLNode_GetProperty(valueNode, "name", NULL));
AQHMQTT_Value_SetName(value, sValueName);
AQHMQTT_Value_SetValueUnits(value, GWEN_XMLNode_GetProperty(valueNode, "units", NULL));
AQHMQTT_Value_SetPath(value, GWEN_XMLNode_GetProperty(valueNode, "path", NULL));
@@ -415,12 +418,12 @@ AQHMQTT_VALUE *_readXmlValue(GWEN_XMLNODE *valueNode)
translationList=_readXmlTranslationList(translationNode);
if (translationList) {
DBG_INFO(NULL, "Translations read");
DBG_ERROR(NULL, "Translations read for value \"%s\"", sValueName);
AQHMQTT_Value_SetTranslationList(value, translationList);
}
}
else {
DBG_INFO(NULL, "No <translations> element");
DBG_ERROR(NULL, "No <translations> element in value %s", sValueName);
}
return value;
@@ -440,7 +443,7 @@ AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode)
if (translation)
AQHMQTT_Translation_List_Add(translation, translationList);
else {
DBG_INFO(NULL, "Error reading <translation> element");
DBG_ERROR(NULL, "Error reading <translation> element");
AQHMQTT_Translation_List_free(translationList);
return NULL;
}
@@ -458,16 +461,16 @@ AQHMQTT_TRANSLATION_LIST *_readXmlTranslationList(GWEN_XMLNODE *parentNode)
AQHMQTT_TRANSLATION *_readXmlTranslation(GWEN_XMLNODE *translationNode)
{
const char *sAqhValue;
int aqhValue;
const char *sDriverValue;
sAqhValue=GWEN_XMLNode_GetProperty(translationNode, "aqhValue", NULL);
aqhValue=GWEN_XMLNode_GetIntProperty(translationNode, "aqhValue", 0);
sDriverValue=GWEN_XMLNode_GetProperty(translationNode, "driverValue", NULL);
if (sAqhValue && *sAqhValue && sDriverValue && *sDriverValue) {
if (sDriverValue && *sDriverValue) {
AQHMQTT_TRANSLATION *translation;
translation=AQHMQTT_Translation_new();
AQHMQTT_Translation_SetAqhValue(translation, sAqhValue);
AQHMQTT_Translation_SetAqhValue(translation, aqhValue);
AQHMQTT_Translation_SetDriverValue(translation, sDriverValue);
return translation;
}

View File

@@ -222,7 +222,7 @@ void _writeValueToXml(const AQHMQTT_VALUE *value, GWEN_XMLNODE *node)
void _writeTranslationToXml(const AQHMQTT_TRANSLATION *t, GWEN_XMLNODE *nTranslation)
{
_setXmlPropertyIfNotNull(nTranslation, "aqhValue", AQHMQTT_Translation_GetAqhValue(t));
GWEN_XMLNode_SetIntProperty(nTranslation, "aqhValue", AQHMQTT_Translation_GetAqhValue(t));
_setXmlPropertyIfNotNull(nTranslation, "driverValue", AQHMQTT_Translation_GetDriverValue(t));
}

View File

@@ -55,6 +55,7 @@ void AQH_NodeServer_HandleForward(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSA
AQH_Message_SetData(nodeMsg, ptr, len);
AQH_Message_SetUsedSize(nodeMsg, len);
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, nodeMsg);
AQH_NodeServer_WriteTtyMsgToLogFile(o, nodeMsg, "sending");
DBG_ERROR(NULL, "Forwarding node message %d to node", AQH_NodeMessage_GetMsgType(nodeMsg));
}
else {

View File

@@ -54,7 +54,7 @@ void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESS
AQH_Endpoint_GetNextMessageId(ep), AQH_IpcMessage_GetMsgId(msg),
niNext?0:AQH_MSGNODE_GETDEVICES_RSP_FLAGS_LASTMSG, ni);
AQH_Endpoint_AddMsgOut(ep, outMsg);
DBG_ERROR(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
DBG_DEBUG(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
ni=niNext;
}
}

View File

@@ -92,69 +92,61 @@ void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSA
varName=AQH_Value_GetName(value);
if (varName) {
char *data;
double data;
AQH_NODE_INFO *nodeInfo;
data=AQH_IpcdMessageSetData_ReadData(tagList);
if (data) {
AQH_NODE_INFO *nodeInfo;
nodeInfo=_getNodeInfoFromValue(xo, value);
if (nodeInfo) {
const char *devName;
nodeInfo=_getNodeInfoFromValue(xo, value);
if (nodeInfo) {
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AQH_NodeServer_GetDeviceDefByName(o, devName);
if (devInfo) {
const AQHNODE_VALUE *devValue;
devInfo=AQH_NodeServer_GetDeviceDefByName(o, devName);
if (devInfo) {
const AQHNODE_VALUE *devValue;
devValue=AQHNODE_Value_List_GetByName(AQHNODE_Device_GetValueList(devInfo), varName);
if (devValue) {
uint16_t dataVal=0;
uint16_t dataDenom=0;
devValue=AQHNODE_Value_List_GetByName(AQHNODE_Device_GetValueList(devInfo), varName);
if (devValue) {
uint16_t dataVal=0;
uint16_t dataDenom=0;
if (AQH_ReadDataFromDouble(AQHNODE_Value_GetDataType(devValue), data, &dataVal, &dataDenom)==0) {
AQH_MSG_REQUEST *rq;
int destAddr;
if (AQH_ReadDataFromString(AQHNODE_Value_GetDataType(devValue), data, &dataVal, &dataDenom)==0) {
AQH_MSG_REQUEST *rq;
int destAddr;
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
DBG_DEBUG(NULL, "Creating SETDATA request");
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
DBG_DEBUG(NULL, "Creating SETDATA request");
rq=_mkRequest_SetData(o, xo, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
AQH_NodeServer_AddRequestToTree(o, rq);
/* done */
}
else {
DBG_ERROR(NULL, "Bad data \"%s\"", data);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
rq=_mkRequest_SetData(o, xo, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
AQH_NodeServer_AddRequestToTree(o, rq);
/* done */
}
else {
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
DBG_ERROR(NULL, "Bad data \"%.2f\"", data);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
else {
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_TRYAGAIN);
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "No matching nodeinfo");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_TRYAGAIN);
}
free(data);
}
else {
DBG_ERROR(NULL, "No data");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
DBG_ERROR(NULL, "No matching nodeinfo");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
@@ -274,6 +266,7 @@ AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
msgOut=AQH_ValueMessage_new(destAddr, xo->nodeAddress, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom);
AQH_NodeServer_WriteTtyMsgToLogFile(o, msgOut, "sending");
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, msgOut);
return rq;

View File

@@ -41,6 +41,7 @@
#include <aqhome/msg/node/m_value.h>
#include <aqhome/msg/node/m_recvstats.h>
#include <aqhome/msg/node/m_sendstats.h>
#include <aqhome/msg/node/m_memstats.h>
#include <aqhome/data/value.h>
#include <gwenhywfar/args.h>
@@ -122,7 +123,9 @@ static void _forwardTtyMsgToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH
static void _forwardValueMessageToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromSendStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromRecvStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromMemStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardTtyMsgToClients(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _publishIntWithIdx(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int idx, int vModality, const char *vUnits, int v);
static void _publishInt(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v);
static void _publishDouble(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, double v);
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
@@ -827,9 +830,11 @@ void _forwardTtyMsgToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAG
code=AQH_NodeMessage_GetMsgType(msg);
switch(code) {
case AQH_MSG_TYPE_VALUE_REPORT: _forwardValueMessageToBroker(o, xo, msg); break;
case AQH_MSG_TYPE_COMSENDSTATS: _forwardDataFromSendStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_COMRECVSTATS: _forwardDataFromRecvStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_VALUE_REPORT: _forwardValueMessageToBroker(o, xo, msg); break;
case AQH_MSG_TYPE_COMSENDSTATS: _forwardDataFromSendStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_COMRECVSTATS: _forwardDataFromRecvStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_MEMSTATS: _forwardDataFromMemStatsMsgToBroker(xo, msg); break;
default: break;
}
}
@@ -898,24 +903,21 @@ void _forwardDataFromSendStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE
packetsOutInt=AQH_SendStatsMessage_GetPacketsOut(msg);
if (packetsOutInt) {
uint32_t uid;
double packetsOut;
double collisions;
double busy;
double collisionsPercentage=0.0;
double busyPercentage=0.0;
int devNum;
uid=AQH_SendStatsMessage_GetUid(msg);
packetsOut=/*(double)*/ packetsOutInt;
collisions=/*(double)*/ AQH_SendStatsMessage_GetCollisions(msg);
busy=/*(double)*/ AQH_SendStatsMessage_GetBusyErrors(msg);
devNum=AQH_SendStatsMessage_GetInterface(msg);
collisionsPercentage=collisions*100.0/packetsOut;
busyPercentage=busy*100.0/packetsOut;
_publishInt( xo, uid, "net/packetsOut", 0, NULL, packetsOutInt);
_publishInt( xo, uid, "net/collisions", 0, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishDouble(xo, uid, "net/collisionsPercent", 0, "%", collisionsPercentage);
_publishDouble(xo, uid, "net/busyPercent", 0, "%", busyPercentage);
if (devNum==0) {
_publishInt(xo, uid, "net/packetsOut", AQH_ValueModality_Stats, NULL, packetsOutInt);
_publishInt(xo, uid, "net/collisions", AQH_ValueModality_Stats, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishInt(xo, uid, "net/busy", AQH_ValueModality_Stats, NULL, (int) AQH_SendStatsMessage_GetBusyErrors(msg));
}
else {
_publishIntWithIdx(xo, uid, "net/packetsOut", devNum, AQH_ValueModality_Stats, NULL, packetsOutInt);
_publishIntWithIdx(xo, uid, "net/collisions", devNum, AQH_ValueModality_Stats, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishIntWithIdx(xo, uid, "net/busy", devNum, AQH_ValueModality_Stats, NULL, (int) AQH_SendStatsMessage_GetBusyErrors(msg));
}
}
}
@@ -928,30 +930,57 @@ void _forwardDataFromRecvStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE
packetsInInt=AQH_RecvStatsMessage_GetPacketsIn(msg);
if (packetsInInt) {
uint32_t uid;
double packetsIn;
double crcErrors;
double ioErrors;
double crcErrorsPercentage=0.0;
double ioErrorsPercentage=0.0;
int devNum;
int m=AQH_ValueModality_Stats;
uid=AQH_SendStatsMessage_GetUid(msg);
packetsIn=/*(double)*/ packetsInInt;
crcErrors=/*(double)*/AQH_RecvStatsMessage_GetCrcErrors(msg);
ioErrors=/*(double)*/AQH_RecvStatsMessage_GetIoErrors(msg);
uid=AQH_RecvStatsMessage_GetUid(msg);
devNum=AQH_RecvStatsMessage_GetInterface(msg);
crcErrorsPercentage=crcErrors*100.0/packetsIn;
ioErrorsPercentage=ioErrors*100.0/packetsIn;
_publishInt( xo, uid, "net/packetsIn", 0, NULL, packetsInInt);
_publishInt( xo, uid, "net/crcerrors", 0, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishInt( xo, uid, "net/ioerrors", 0, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishDouble(xo, uid, "net/crcerrorsPercent", 0, "%", crcErrorsPercentage);
_publishDouble(xo, uid, "net/ioerrorsPercent", 0, "%", ioErrorsPercentage);
if (devNum==0) {
_publishInt(xo, uid, "net/packetsIn", m, NULL, packetsInInt);
_publishInt(xo, uid, "net/crcErrors", m, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishInt(xo, uid, "net/ioErrors", m, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishInt(xo, uid, "net/nobufferErrors", m, NULL, (int) AQH_RecvStatsMessage_GetNoBufferErrors(msg));
_publishInt(xo, uid, "net/msgSizeErrors", m, NULL, (int) AQH_RecvStatsMessage_GetMsgSizeErrors(msg));
_publishInt(xo, uid, "net/missed", m, NULL, (int) AQH_RecvStatsMessage_GetMissed(msg));
}
else {
_publishIntWithIdx(xo, uid, "net/packetsIn", devNum, m, NULL, packetsInInt);
_publishIntWithIdx(xo, uid, "net/crcErrors", devNum, m, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishIntWithIdx(xo, uid, "net/ioErrors", devNum, m, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishIntWithIdx(xo, uid, "net/nobufferErrors", devNum, m, NULL, (int) AQH_RecvStatsMessage_GetNoBufferErrors(msg));
_publishIntWithIdx(xo, uid, "net/msgSizeErrors", devNum, m, NULL, (int) AQH_RecvStatsMessage_GetMsgSizeErrors(msg));
_publishIntWithIdx(xo, uid, "net/missed", devNum, m, NULL, (int) AQH_RecvStatsMessage_GetMissed(msg));
}
}
}
void _forwardDataFromMemStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
uint32_t uid;
uid=AQH_MemStatsMessage_GetUid(msg);
_publishInt( xo, uid, "mem/buffersUsed", AQH_ValueModality_Stats, NULL, AQH_MemStatsMessage_GetBuffersUsed(msg));
_publishInt( xo, uid, "mem/maxBuffersUsed", AQH_ValueModality_Stats, NULL, AQH_MemStatsMessage_GetMaxBuffersUsed(msg));
}
void _publishIntWithIdx(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int idx, int vModality, const char *vUnits, int v)
{
GWEN_BUFFER *tbuf;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(tbuf, "%s%d", vPath, idx);
_publishInt(xo, uid, GWEN_Buffer_GetStart(tbuf), vModality, vUnits, v);
GWEN_Buffer_free(tbuf);
}
void _publishInt(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v)
{
_publishDouble(xo, uid, vPath, vModality, vUnits, /*(double)*/ v);
@@ -980,7 +1009,7 @@ void _publishDouble(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vM
AQH_MESSAGE *pubMsg;
pubMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_UPDATEDATA,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0,
AQH_Endpoint_GetNextMessageId(xo->brokerEndpoint), 0, 0,
value, arrayToSend, 1);
if (pubMsg) {
DBG_INFO(AQH_LOGDOMAIN,
@@ -1017,6 +1046,30 @@ void _setDeviceName(AQH_VALUE *value, uint32_t uid)
void AQH_NodeServer_WriteTtyMsgToLogFile(AQH_OBJECT *o, const AQH_MESSAGE *msg, const char *txt)
{
if (o && msg) {
AQH_NODE_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_NODE_SERVER, o);
if (xo && xo->logFile) {
GWEN_BUFFER *dbuf;
GWEN_TIME *ti;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
ti=GWEN_CurrentTime();
GWEN_Time_toString(ti, "YYYY-MM-DD hh:mm:ss ", dbuf);
GWEN_Time_free(ti);
ti=NULL;
AQH_NodeMessage_DumpSpecificToBuffer(msg, dbuf, txt?txt:"");
_writeToLogFile(xo->logFile, GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
}
}
void _writeTtyMsgToLogFile(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
if (xo->logFile) {

View File

@@ -86,6 +86,7 @@ AQH_MSG_REQUEST *AQH_NodeServer_GetRequestTree(const AQH_OBJECT *o);
void AQH_NodeServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq);
void AQH_NodeServer_CleanupRequests(AQH_OBJECT *o);
void AQH_NodeServer_WriteTtyMsgToLogFile(AQH_OBJECT *o, const AQH_MESSAGE *msg, const char *txt);

View File

@@ -26,15 +26,6 @@
<unit id="and1" type="and" />
<unit id="zeroPosNegString1" type="zeroPosNegString">
<params>
<param name="valueIfNegative">OFF</param>
<param name="valueIfZero">OFF</param>
<param name="valueIfPositive">ON</param>
</params>
</unit>
<unit id="valueSetOutPlug1" type="valueSet">
<params>
<param name="valueName">mqtt/109C2F/power</param>
@@ -49,7 +40,6 @@
<link sourceUnit=".timer" sourcePort="output" targetUnit="suntime1" targetPort="timer" />
<link sourceUnit="suntime1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="delayedOff1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="and1" sourcePort="output" targetUnit="zeroPosNegString1" targetPort="input" />
<link sourceUnit="zeroPosNegString1" sourcePort="output" targetUnit="valueSetOutPlug1" targetPort="input" />
<link sourceUnit="and1" sourcePort="output" targetUnit="valueSetOutPlug1" targetPort="input" />
</links>
</network>

View File

@@ -12,7 +12,7 @@
</inputPorts>
<outputPorts>
<outputPort name="output" dataType="string" />
<outputPort name="output" dataType="double" />
</outputPorts>

View File

@@ -349,13 +349,35 @@ const char *_readDouble(const char *s, double *ptrDouble)
while(*s && isspace(*s))
s++;
sStart=s;
while(*s && (isdigit(*s) || *s=='.' || *s=='+' || *s=='-'))
s++;
if (*s=='#') {
uint32_t v=0;
rv=GWEN_Text_StringToDouble(sStart, ptrDouble);
if (rv<0) {
DBG_ERROR(NULL, "Error reading double: %d", rv);
return NULL;
s++;
while(*s) {
unsigned char c;
c=tolower(*s);
if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
c-='0';
if (c>9)
c-=7;
v<<=4;
v+=c;
}
else
break;
s++;
}
*ptrDouble=(double) v;
}
else {
while(*s && (isdigit(*s) || *s=='.' || *s=='+' || *s=='-'))
s++;
rv=GWEN_Text_StringToDouble(sStart, ptrDouble);
if (rv<0) {
DBG_ERROR(NULL, "Error reading double: %d", rv);
return NULL;
}
}
return s;

View File

@@ -38,7 +38,6 @@
*/
static void _cbInputData(AQHREACT_UNIT *unit, AQHREACT_PORT *port, const AQHREACT_DATAOBJECT *dataObject);
static AQH_MESSAGE *_mkSetDataMsgString(AQH_OBJECT *brokerEndpoint, const char *sValueName, const AQHREACT_DATAOBJECT *dataObject);
static AQH_MESSAGE *_mkSetDataMsgDouble(AQH_OBJECT *brokerEndpoint, const char *sValueName, const AQHREACT_DATAOBJECT *dataObject);
@@ -62,7 +61,7 @@ AQHREACT_UNIT *AqHomeReact_UnitValueSet_new(AQH_OBJECT *aqh)
port=AQHREACT_Port_new();
AQHREACT_Port_SetName(port, "input");
AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_VALUESET_INSLOT_VALUE);
AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_STRING);
AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE);
AQHREACT_Unit_AddInputPort(unit, port);
param=AQHREACT_Param_new();
@@ -92,9 +91,6 @@ void _cbInputData(AQHREACT_UNIT *unit, AQHREACT_PORT *port, const AQHREACT_DATAO
case AQHREACT_DATAOBJECTTYPE_DOUBLE:
msgOut=_mkSetDataMsgDouble(brokerEndpoint, sValueName, dataObject);
break;
case AQHREACT_DATAOBJECTTYPE_STRING:
msgOut=_mkSetDataMsgString(brokerEndpoint, sValueName, dataObject);
break;
default:
DBG_ERROR(NULL, "Unhandled data type (%d)", AQHREACT_DataObject_GetDataType(dataObject));
msgOut=NULL;
@@ -130,47 +126,20 @@ void _cbInputData(AQHREACT_UNIT *unit, AQHREACT_PORT *port, const AQHREACT_DATAO
AQH_MESSAGE *_mkSetDataMsgString(AQH_OBJECT *brokerEndpoint, const char *sValueName, const AQHREACT_DATAOBJECT *dataObject)
{
AQH_MESSAGE *msgOut;
AQH_VALUE *v;
v=AQH_Value_new();
AQH_Value_SetNameForSystem(v, sValueName);
msgOut=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA,
AQH_Endpoint_GetNextMessageId(brokerEndpoint), 0,
v, AQHREACT_DataObject_GetStringData(dataObject));
AQH_Value_free(v);
return msgOut;
}
AQH_MESSAGE *_mkSetDataMsgDouble(AQH_OBJECT *brokerEndpoint, const char *sValueName, const AQHREACT_DATAOBJECT *dataObject)
{
AQH_MESSAGE *msgOut;
AQH_VALUE *v;
double data;
GWEN_BUFFER *buf;
int rv;
v=AQH_Value_new();
AQH_Value_SetNameForSystem(v, sValueName);
data=AQHREACT_DataObject_GetDoubleData(dataObject);
buf=GWEN_Buffer_new(0, 64, 0, 1);
rv=GWEN_Text_DoubleToBuffer(data, buf);
if (rv<0) {
GWEN_Buffer_free(buf);
AQH_Value_free(v);
return NULL;
}
msgOut=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA,
AQH_Endpoint_GetNextMessageId(brokerEndpoint), 0,
v, GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
v, data);
AQH_Value_free(v);
return msgOut;
}

View File

@@ -6,6 +6,7 @@
<includes type="c" >
$(gwenhywfar_cflags)
$(aqdiagram_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
</includes>
@@ -54,6 +55,7 @@
<libraries>
$(gwenhywfar_libs)
$(aqdiagram_libs)
</libraries>
<subdirs>

View File

@@ -43,6 +43,8 @@
setdata.h
moddevice.h
watch.h
devicestate.h
imgperioddata.h
</headers>
<sources>
@@ -58,6 +60,8 @@
setdata.c
moddevice.c
watch.c
devicestate.c
imgperioddata.c
</sources>
<useTargets>

View File

@@ -197,7 +197,7 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
AQH_Value_SetValueUnits(v, valueUnits);
AQH_Value_SetDeviceName(v, deviceName);
msg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, v, timestampToSend, dataToSend);
msg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, 0, v, timestampToSend, dataToSend);
AQH_Value_free(v);
GWEN_JsonElement_free(jRoot);
return msg;

View File

@@ -0,0 +1,264 @@
/****************************************************************************
* 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 "./devicestate.h"
#include "../utils.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/timestamp.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _runCommand(AQH_DATACLIENT *dc);
static void _handleDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *device);
static void _handleValue(AQH_DATACLIENT *dc, const AQH_VALUE *value);
static void _printDataPoints(const uint64_t *dataPoints, uint32_t numValues);
static void _printSingleDataPoint(uint64_t timestamp, double data);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_DeviceState(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_CHAR, "device", 1, 1, "d", "device", I18S("device name"), NULL},
{ A_ARG, A_CHAR, "valueName", 0, 1, "N", "valuename", I18S("Value name for device(e.g. LIGHT)"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_runCommand(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _runCommand(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
const char *deviceName;
AQH_DEVICE_LIST *deviceList;
AQH_DEVICE *device;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
deviceName=GWEN_DB_GetCharValue(dbLocalArgs, "device", 0, "*");
deviceList=AQH_DataClient_GetDevices(dc, deviceName);
if (deviceList==NULL) {
DBG_ERROR(NULL, "Error getting devices");
return GWEN_ERROR_GENERIC;
}
device=AQH_Device_List_First(deviceList);
while(device) {
const char *s;
s=AQH_Device_GetNameForSystem(device);
if (s && *s && -1!=GWEN_Text_ComparePattern(s, deviceName, 0)) {
_handleDevice(dc, device);
}
device=AQH_Device_List_Next(device);
}
AQH_Device_List_free(deviceList);
return 0;
}
void _handleDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *device)
{
AQH_VALUE_LIST *valueList;
const char *devName;
const char *roomName;
const char *location;
const char *descr;
devName=AQH_Device_GetNameForSystem(device);
roomName=AQH_Device_GetRoomName(device);
location=AQH_Device_GetLocation(device);
descr=AQH_Device_GetDescription(device);
fprintf(stdout, "%s (room: %s, loc: %s, descr: %s)\n",
devName,
roomName?roomName:"--",
location?location:"--",
descr?descr:"--");
valueList=AQH_DataClient_GetValues(dc, devName, 0);
if (valueList) {
const AQH_VALUE *value;
value=AQH_Value_List_First(valueList);
while(value) {
if (AQH_Value_GetValueType(value)==AQH_ValueType_Sensor)
_handleValue(dc, value);
value=AQH_Value_List_Next(value);
}
}
AQH_Value_List_free(valueList);
}
void _handleValue(AQH_DATACLIENT *dc, const AQH_VALUE *value)
{
GWEN_DB_NODE *dbLocalArgs;
const char *wantedValueName;
const char *valueName;
int numDataPoints;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
numDataPoints=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 5);
wantedValueName=GWEN_DB_GetCharValue(dbLocalArgs, "valueName", 0, "*");
valueName=AQH_Value_GetName(value);
if (valueName &&
-1==GWEN_Text_ComparePattern(valueName, "stats_*", 0) &&
-1!=GWEN_Text_ComparePattern(valueName, wantedValueName, 0)) {
const char *valueNameForSystem;
uint64_t *dataPoints;
uint64_t recvdNum;
valueNameForSystem=AQH_Value_GetNameForSystem(value);
fprintf(stdout, " %s: ", valueName?valueName:"<empty>");
dataPoints=malloc(numDataPoints*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetLastData(dc, valueNameForSystem, dataPoints, numDataPoints);
if (recvdNum>0)
_printDataPoints(dataPoints, recvdNum);
free(dataPoints);
fprintf(stdout, "\n");
}
}
void _printDataPoints(const uint64_t *dataPoints, uint32_t numValues)
{
uint32_t i;
for(i=0; i<numValues; i++) {
uint64_t timestamp;
union {double f; uint64_t i;} u;
if (i)
fprintf(stdout, " | ");
timestamp=*(dataPoints++);
u.i=*(dataPoints++);
_printSingleDataPoint(timestamp, u.f);
}
}
void _printSingleDataPoint(uint64_t timestamp, double data)
{
GWEN_TIMESTAMP *ts;
ts=GWEN_Timestamp_fromLocalTime((time_t) timestamp);
if (ts)
fprintf(stdout, "%lf (%04d/%02d/%02d-%02d:%02d:%02d)",
data,
GWEN_Timestamp_GetYear(ts),
GWEN_Timestamp_GetMonth(ts),
GWEN_Timestamp_GetDay(ts),
GWEN_Timestamp_GetHour(ts),
GWEN_Timestamp_GetMinute(ts),
GWEN_Timestamp_GetSecond(ts));
else
fprintf(stdout, "%lf", data);
}

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_TOOL_DEVICESTATE_H
#define AQHOME_TOOL_DEVICESTATE_H
#include <gwenhywfar/db.h>
int AQH_Tool_DeviceState(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -0,0 +1,299 @@
/****************************************************************************
* 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 "./imgperioddata.h"
#include "../utils.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/dataclient/client.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/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _createGraph(AQH_DATACLIENT *dc);
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
static AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
uint64_t num, uint64_t tsBegin, uint64_t tsEnd);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_ImgPeriodData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ A_ARG, A_CHAR, "tsBegin", 0, 1, "tb", "tsbegin", I18S("Timestamp range begin"), NULL},
{ A_ARG, A_CHAR, "tsEnd", 0, 1, "te", "tsend", I18S("Timestamp range end"), NULL},
{ A_ARG, A_CHAR, "imgFile", 1, 1, "F", NULL, I18S("Image path"), NULL},
{ A_ARG, A_INT, "imgWidth", 0, 1, "W", NULL, I18S("Image width"), NULL},
{ A_ARG, A_INT, "imgHeight", 0, 1, "H", NULL, I18S("Image height"), NULL},
{ A_ARG, A_CHAR, "upperLimit", 0, 1, "U", NULL, I18S("Upper limit for y axis"), NULL},
{ A_ARG, A_CHAR, "lowerLimit", 0, 1, "L", NULL, I18S("Lower limit for y axis"), NULL},
{ A_ARG, A_INT, "precision", 0, 1, "yp", NULL, I18S("Y axis Data Precision (default: 2)"), NULL},
{ A_ARG, A_CHAR, "graphTitle", 0, 1, "gt", NULL, I18S("Graph title"), NULL},
{ A_ARG, A_CHAR, "curveSpec", 1, 64, "G", NULL, I18S("Curve specification"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv!=0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_createGraph(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _createGraph(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
int numDataPoints;
uint64_t tsBegin;
uint64_t tsEnd;
const char *imgFile;
int imgWidth;
int imgHeight;
int precision;
const char *s;
uint32_t tickFlags=0;
double upperLimit;
double lowerLimit;
const char *graphTitle;
AQDG_GRAPH *g;
AQDG_DRAW_CONTEXT *drawContext;
AQDG_OBJECT *graphObject;
int i;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
numDataPoints=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 10000); /* remove later */
imgFile=GWEN_DB_GetCharValue(dbLocalArgs, "imgFile", 0, NULL);
imgWidth=GWEN_DB_GetIntValue(dbLocalArgs, "imgWidth", 0, 800);
imgHeight=GWEN_DB_GetIntValue(dbLocalArgs, "imgHeight", 0, 600);
precision=GWEN_DB_GetIntValue(dbLocalArgs, "precision", 0, 2);
graphTitle=GWEN_DB_GetCharValue(dbLocalArgs, "graphTitle", 0, NULL);
tsBegin=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbLocalArgs, "tsBegin", 0, NULL));
if (tsBegin==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad begin timestamp");
return 1;
}
tsEnd=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbLocalArgs, "tsEnd", 0, NULL));
if (tsEnd==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad end timestamp");
return 1;
}
s=GWEN_DB_GetCharValue(dbLocalArgs, "upperLimit", 0, NULL);
if (s && *s) {
if (1==sscanf(s, "%lf", &upperLimit))
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MAXY;
else {
fprintf(stderr, "Invalid upperLimit (%s)\n", s);
return 1;
}
}
s=GWEN_DB_GetCharValue(dbLocalArgs, "lowerLimit", 0, NULL);
if (s && *s) {
if (1==sscanf(s, "%lf", &lowerLimit))
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MINY;
else {
fprintf(stderr, "Invalid lowerLimit (%s)\n", s);
return 1;
}
}
g=AQDG_TimeGraph_new(graphTitle?graphTitle:NULL, NULL, "Value", NULL, precision);
for (i=0; i<16; i++) {
const char *curveSpec;
curveSpec=GWEN_DB_GetCharValue(dbLocalArgs, "curveSpec", i, NULL);
if (curveSpec) {
GWEN_STRINGLIST *sl;
char delim[2];
delim[0]=curveSpec[0];
delim[1]=0;
sl=GWEN_StringList_fromString2(curveSpec+1, delim, 0, GWEN_TEXT_FLAGS_CHECK_BACKSLASH);
if (sl) {
const char *sValue;
const char *sLabel;
const char *sModifier;
sValue=GWEN_StringList_StringAt(sl, 0);
sLabel=GWEN_StringList_StringAt(sl, 1);
sModifier=GWEN_StringList_StringAt(sl, 2);
DBG_DEBUG(NULL, "Value: %s, Label: %s, Modifier: %s", sValue?sValue:"", sLabel?sLabel:"", sModifier?sModifier:"");
if (sValue && *sValue) {
AQDG_GRAPH_DATAPAIR_LIST *dpList;
DBG_DEBUG(NULL, "Requesting data for %s", sValue);
dpList=_requestDataPairList(dc, sValue, numDataPoints, tsBegin, tsEnd);
if (dpList)
AQDG_TimeGraph_ModifyDataAndAddCurve(g, sLabel?sLabel:sValue, sModifier, dpList);
}
GWEN_StringList_free(sl);
}
}
else
break;
}
AQDG_TimeGraph_SetupTicks(g, tickFlags, lowerLimit, upperLimit);
drawContext=AQDG_Draw_ContextCairo_Png_new(imgFile, 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);
return 0;
}
AQDG_GRAPH_DATAPAIR_LIST *_requestDataPairList(AQH_DATACLIENT *dc, const char *valueName,
uint64_t num, uint64_t tsBegin, uint64_t tsEnd)
{
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 {
free(dataPoints);
return NULL;
}
}
AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues)
{
AQDG_GRAPH_DATAPAIR_LIST *dpList;
uint64_t i;
DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues);
dpList=AQDG_Graph_DataPair_List_new();
for(i=0; i<numValues; i++) {
AQDG_GRAPH_DATAPAIR *dp;
double timestamp;
union {double f; uint64_t i;} u;
timestamp=(double)(*(dataPoints++));
u.i=*(dataPoints++);
dp=AQDG_Graph_DataPair_new();
AQDG_Graph_DataPair_SetValueX(dp, timestamp);
AQDG_Graph_DataPair_SetValueY(dp, u.f);
AQDG_Graph_DataPair_List_Add(dp, dpList);
}
AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
return dpList;
}

View File

@@ -0,0 +1,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_TOOL_IMGPERIODDATA_H
#define AQHOME_TOOL_IMGPERIODDATA_H
#include <gwenhywfar/db.h>
int AQH_Tool_ImgPeriodData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -45,6 +45,7 @@
*/
static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
static int _readValueFromString(const char *s, double *pDouble);
@@ -93,18 +94,30 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
GWEN_DB_NODE *dbArgs;
const char *valueName;
const char *valueUnits;
const char *valueData;
const char *valueDataAsString;
double valueData;
AQH_VALUE *v;
int rv;
dbArgs=AQH_ToolClient_GetDbLocalArgs(o);
valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL);
valueUnits=GWEN_DB_GetCharValue(dbArgs, "valueUnits", 0, NULL);
valueData=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL);
valueDataAsString=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL);
if (valueDataAsString && *valueDataAsString) {
rv=_readValueFromString(valueDataAsString, &valueData);
if (rv<0) {
DBG_ERROR(NULL, "ERROR: Bad value");
return NULL;
}
}
if (!(valueName && *valueName)) {
DBG_ERROR(NULL, "ERROR: Missing value name");
return NULL;
}
v=AQH_Value_new();
AQH_Value_SetNameForSystem(v, valueName);
AQH_Value_SetValueUnits(v, valueUnits);
@@ -116,3 +129,40 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
int _readValueFromString(const char *s, double *pDouble)
{
int l;
l=strlen(s);
if (l) {
if (*s=='#') {
unsigned int h;
if (1==sscanf(s+1, "%x", &h)) {
*pDouble=(double) h;
return 0;
}
}
else if (l>1 && s[0]=='0' && ((tolower(s[1])=='x') || tolower(s[1])=='b')) {
unsigned int h;
if (1==sscanf(s, "%u", &h)) {
*pDouble=(double) h;
return 0;
}
}
else {
double d;
if (1==sscanf(s, "%lf", &d)) {
return d;
}
}
DBG_ERROR(NULL, "Bad value \"%s\"", s);
return GWEN_ERROR_GENERIC;
}
}

View File

@@ -23,6 +23,8 @@
#include "./data/setdata.h"
#include "./data/moddevice.h"
#include "./data/watch.h"
#include "./data/devicestate.h"
#include "./data/imgperioddata.h"
#include <aqhome/api.h>
#include <aqhome/aqhome.h>
@@ -101,6 +103,8 @@ int main(int argc, char **argv)
GWEN_FE_DAH("setdata", AQH_Tool_SetData, I18N("Set data for a value on the data server (e.g. a switch or thermostat)")),
GWEN_FE_DAH("moddevice", AQH_Tool_ModDevice, I18N("Modify a device on the data server")),
GWEN_FE_DAH("watch", AQH_Tool_Watch, I18N("Watch and print changes of values on the data server")),
GWEN_FE_DAH("devicestate", AQH_Tool_DeviceState, I18N("Show state of devices")),
GWEN_FE_DAH("imgperioddata", AQH_Tool_ImgPeriodData, I18N("Create diagram of datapoints from a date range")),
GWEN_FE_END(),
};
const GWEN_FUNCS *func;

View File

@@ -51,7 +51,7 @@
#define FLASH_TOOL_MAX_REPEAT 16
#define FLASH_TOOL_DEFAULT_TIMEOUTINSECS 5
#define FLASH_TOOL_WAITFORFLASHREADY_INSECS 30
#define FLASH_TOOL_WAITFORFLASHRESPONSE_SECS 2
#define FLASH_TOOL_WAITFORFLASHRESPONSE_SECS 1

View File

@@ -384,11 +384,31 @@ uint64_t Utils_GetTimeStampFromString(const char *s)
case 'm': x*=60; break;
case 'h': x*=(60*60); break;
case 'd': x*=(60*60*24); break;
case 'w': x*=(60*60*24*7); break;
case 'M': x*=(60*60*24*30); break;
case 'y': x*=(60*60*24*365); break;
default: break;
}
}
return (now-x);
}
if (*s=='@') {
int y, m, d, H, M, S;
if (6==sscanf(s+1, "%d/%d/%d-%d:%d:%d", &y, &m, &d, &H, &M, &S)) {
GWEN_TIMESTAMP *ts;
uint64_t x=0;
ts=GWEN_Timestamp_new(y, m, d, H, M, S);
x=GWEN_Timestamp_toTimeT(ts);
GWEN_Timestamp_free(ts);
return x;
}
else {
DBG_ERROR(NULL, "Invalid timespec [%s], expected: @YYYY/MM/DD-HH:MM:SS", s);
return (uint64_t) (-1);
}
}
else {
unsigned long int x;

View File

@@ -1,6 +1,6 @@
#!/bin/bash
#export AQHOME_LOGLEVEL=info
export AQHOME_LOGLEVEL=info
export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH"
# 0-build/apps/aqhome-storage/aqhome-storage $*

View File

@@ -43,6 +43,10 @@ static void _definePath(const char *pathName, const char *pathValue);
static GWEN_STRINGLIST *_getListOfMatchingFiles(const char *pathName, const char *subFolder, const char *mask);
static GWEN_BUFFER *_getRuntimeFilePath(const char *pathName, const char *sFilename);
static GWEN_BUFFER *_findFileinPath(const char *pathName, const char *sFilename);
static int _readUint8DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom);
static int _readUint16DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom);
static int _readUint32DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom);
static int _readUint8DataFromString(const char *s, uint16_t *pDataVal, uint16_t *pDataDenom);
static int _readUint16DataFromString(const char *s, uint16_t *pDataVal, uint16_t *pDataDenom);
static int _readUint32DataFromString(const char *s, uint16_t *pDataVal, uint16_t *pDataDenom);
@@ -269,6 +273,10 @@ int AQH_ValueModality_fromString(const char *s)
return AQH_ValueModality_Stats;
else if (strcasecmp(s, "light")==0)
return AQH_ValueModality_Light;
else if (strcasecmp(s, "onOff")==0)
return AQH_ValueModality_OnOff;
else if (strcasecmp(s, "onOffAuto")==0)
return AQH_ValueModality_OnOffAuto;
}
return AQH_ValueModality_Unknown;
}
@@ -288,6 +296,8 @@ const char *AQH_ValueModality_toString(int i)
case AQH_ValueModality_TVOC: return "tvoc";
case AQH_ValueModality_Stats: return "stats";
case AQH_ValueModality_Light: return "light";
case AQH_ValueModality_OnOff: return "onOff";
case AQH_ValueModality_OnOffAuto: return "onOffAuto";
case AQH_ValueModality_Unknown:
default: return "unknown";
}
@@ -463,6 +473,59 @@ GWEN_BUFFER *_findFileinPath(const char *pathName, const char *sFilename)
int AQH_ReadDataFromDouble(int dataType, double d, uint16_t *pDataVal, uint16_t *pDataDenom)
{
switch(dataType) {
case AQH_ValueDataType_Uint8: return _readUint8DataFromDouble(d, pDataVal, pDataDenom);
case AQH_ValueDataType_Int:
case AQH_ValueDataType_Uint16: return _readUint16DataFromDouble(d, pDataVal, pDataDenom);
case AQH_ValueDataType_Uint32: return _readUint32DataFromDouble(d, pDataVal, pDataDenom);
case AQH_ValueDataType_Rational: break;
default: break;
}
return GWEN_ERROR_INVALID;
}
int _readUint8DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom)
{
uint8_t v;
v=((uint8_t) (d)) & 0xff;
*pDataVal=v & 0xff;
*pDataDenom=1;
return 0;
}
int _readUint16DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom)
{
uint16_t v;
v=((uint16_t) d) & 0xffff;
*pDataVal=v;
*pDataDenom=1;
return 0;
}
int _readUint32DataFromDouble(double d, uint16_t *pDataVal, uint16_t *pDataDenom)
{
uint32_t v;
v=((uint32_t) d) & 0xffffffff;
*pDataVal=(v>>16) & 0xffff;
*pDataDenom=v & 0xffff;
return 0;
}
int AQH_ReadDataFromString(int dataType, const char *s, uint16_t *pDataVal, uint16_t *pDataDenom)
{
if (s && *s) {

View File

@@ -46,7 +46,9 @@ enum {
AQH_ValueModality_Co2,
AQH_ValueModality_TVOC,
AQH_ValueModality_Stats,
AQH_ValueModality_Light
AQH_ValueModality_Light,
AQH_ValueModality_OnOff,
AQH_ValueModality_OnOffAuto
};
@@ -82,6 +84,7 @@ AQHOME_API int AQH_ValueModality_fromString(const char *s);
AQHOME_API const char *AQH_ValueModality_toString(int i);
AQHOME_API int AQH_ReadDataFromString(int dataType, const char *s, uint16_t *pDataVal, uint16_t *pDataDenom);
AQHOME_API int AQH_ReadDataFromDouble(int dataType, double d, uint16_t *pDataVal, uint16_t *pDataDenom);
#endif

View File

@@ -23,7 +23,7 @@
#define AQH_STORAGE_DATAPOINTS_STEPS 128
#define AQH_STORAGE_MAXOPENFILES 128
/* ------------------------------------------------------------------------------------------------
@@ -410,7 +410,7 @@ uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t
return NULL;
}
numEntries=AQH_DataFile_GetNumberOfEntries(df);
if (maxDataPointsRequested>numEntries)
if (maxDataPointsRequested>numEntries || maxDataPointsRequested==0)
maxDataPointsRequested=numEntries;
arrayLen=(maxDataPointsRequested*2)+1;
arrayPtr=(uint64_t*) malloc(arrayLen*sizeof(uint64_t));
@@ -435,7 +435,8 @@ uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t
if ((fromTime==0 || ts>=fromTime) && (toTime==0 || ts<=toTime)) {
if ((arrayPos+1)>arrayLen) {
DBG_INFO(AQH_LOGDOMAIN, "Limit for number of returned entries reached");
DBG_INFO(AQH_LOGDOMAIN, "Limit for number of returned entries reached (%d, numEntries=%d)",
(int) arrayLen, (int) numEntries);
break;
}
@@ -650,6 +651,16 @@ AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
DBG_ERROR(AQH_LOGDOMAIN, "Error opening/creating datafile for valueId \"%lu\"", (unsigned long int) valueId);
return NULL;
}
if (AQH_DataFile_List_GetCount(sto->dataFileList)>=AQH_STORAGE_MAXOPENFILES) {
AQH_DATAFILE *dfLast;
dfLast=AQH_DataFile_List_Last(sto->dataFileList);
if (dfLast) {
AQH_DataFile_Close(dfLast);
AQH_DataFile_List_Del(dfLast);
AQH_DataFile_free(dfLast);
}
}
DBG_DEBUG(AQH_LOGDOMAIN, "Adding datafile for valueId \"%lu\" to list", (unsigned long int) valueId);
AQH_DataFile_List_Add(df, sto->dataFileList);
}
@@ -661,8 +672,17 @@ AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
AQH_DATAFILE *_findDataFileByValueId(const AQH_STORAGE *sto, uint64_t valueId)
{
if (sto && sto->dataFileList)
return AQH_DataFile_List_GetByValueId(sto->dataFileList, valueId);
if (sto && sto->dataFileList) {
AQH_DATAFILE *df;
df=AQH_DataFile_List_GetByValueId(sto->dataFileList, valueId);
if (df) {
/* move to front of list */
AQH_DataFile_List_Del(df);
AQH_DataFile_List_Insert(df, sto->dataFileList);
return df;
}
}
return NULL;
}

View File

@@ -22,6 +22,7 @@
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/data/m_ipcd_getvalues.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdevices.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
@@ -113,6 +114,21 @@ int AQH_DataClient_ReadLocalArgs(AQH_DATACLIENT *dc,
int AQH_DataClient_ReadConfigFile(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbConfig;
dbConfig=AQH_LoadConfigFile();
if (dbConfig) {
GWEN_DB_Group_free(dc->dbLocalArgs);
dc->dbLocalArgs=dbConfig;
return 0;
}
return GWEN_ERROR_GENERIC;
}
GWEN_DB_NODE *AQH_DataClient_GetDbLocalArgs(const AQH_DATACLIENT *dc)
{
return dc?dc->dbLocalArgs:NULL;
@@ -173,7 +189,7 @@ void AQH_DataClient_SetTimeout(AQH_DATACLIENT *dc, int i)
AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc)
AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc, const char *deviceName)
{
if (dc) {
AQH_MESSAGE *msgOut;
@@ -183,7 +199,7 @@ AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc)
fullDeviceList=AQH_Device_List_new();
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcMessage_new(dc->protoId, dc->protoVer, AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ, msgId, 0, 0, NULL);
msgOut=AQH_IpcdMessageGetDevices_new(AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ, msgId, 0, deviceName);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
while( (msgIn=AQH_IpcEndpoint_WaitForResponseMsg(dc->ipcEndpoint, msgId, dc->timeoutInSeconds)) ) {
@@ -327,7 +343,7 @@ uint64_t AQH_DataClient_GetPeriodData(AQH_DATACLIENT *dc, const char *valueName,
int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, const char *data)
int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, double data)
{
if (dc) {
AQH_MESSAGE *msgOut;
@@ -352,7 +368,25 @@ int AQH_DataClient_UpdateData(AQH_DATACLIENT *dc, const AQH_VALUE *v, uint64_t t
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, v, timeStamp, dataPoint);
msgOut=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, 0, v, timeStamp, dataPoint);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleResult(dc, msgId);
}
return GWEN_ERROR_INVALID;
}
int AQH_DataClient_ModDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *d)
{
if (dc) {
AQH_MESSAGE *msgOut;
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageDevices_newForOneDevice(AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ, msgId, 0, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, d);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleResult(dc, msgId);
@@ -445,7 +479,9 @@ uint64_t _handleDataResponses(AQH_DATACLIENT *dc, uint64_t *dataPtr, uint64_t ma
if (code==AQH_MSGTYPE_IPC_DATA_GETDATA_RSP) {
const uint64_t *recvDataPtr;
uint64_t recvNumberOfPoints;
uint32_t flags;
flags=AQH_IpcdMessageMultiData_GetFlags(tagList);
AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &recvDataPtr, &recvNumberOfPoints);
if (recvNumberOfPoints) {
uint64_t i;
@@ -462,12 +498,14 @@ uint64_t _handleDataResponses(AQH_DATACLIENT *dc, uint64_t *dataPtr, uint64_t ma
}
}
}
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
break;
if (flags & AQH_MSGDATA_MULTIDATA_FLAGS_LASTMSG) {
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
break;
}
}
else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) {
DBG_ERROR(NULL, "Server Error: %d", AQH_IpcMessageResult_GetResult(tagList));
DBG_INFO(NULL, "Server Error: %d", AQH_IpcMessageResult_GetResult(tagList));
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
return 0;
@@ -538,7 +576,7 @@ int AQH_DataClient_ConnectWithArgs(AQH_DATACLIENT *dc, uint32_t flags)
brokerPort=GWEN_DB_GetIntValue(dc->dbLocalArgs, "brokerPort", 0, -1);
if (brokerPort<0)
brokerPort=GWEN_DB_GetIntValue(dc->dbLocalArgs, "ConfigFile/brokerPort", 0, 45456);
brokerPort=GWEN_DB_GetIntValue(dc->dbLocalArgs, "ConfigFile/brokerPort", 0, 1899);
userId=GWEN_DB_GetCharValue(dc->dbLocalArgs, "userId", 0, NULL);
passwd=GWEN_DB_GetCharValue(dc->dbLocalArgs, "password", 0, NULL);

View File

@@ -34,7 +34,7 @@ AQHOME_API int AQH_DataClient_Connect(AQH_DATACLIENT *dc,
AQHOME_API int AQH_DataClient_Disconnect(AQH_DATACLIENT *dc);
AQHOME_API AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc);
AQHOME_API AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc, const char *deviceName);
AQHOME_API AQH_VALUE_LIST *AQH_DataClient_GetValues(AQH_DATACLIENT *dc, const char *deviceName, int modality);
AQHOME_API uint64_t AQH_DataClient_GetFirstData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum);
@@ -43,13 +43,16 @@ AQHOME_API uint64_t AQH_DataClient_GetPeriodData(AQH_DATACLIENT *dc, const char
uint64_t *dataPtr, uint64_t maxNum,
uint64_t tsBegin, uint64_t tsEnd);
AQHOME_API int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, const char *data);
AQHOME_API int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, double data);
AQHOME_API int AQH_DataClient_UpdateData(AQH_DATACLIENT *dc, const AQH_VALUE *v, uint64_t timeStamp, double dataPoint);
AQHOME_API int AQH_DataClient_ModDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *d);
AQHOME_API int AQH_DataClient_ReadLocalArgs(AQH_DATACLIENT *dc,
GWEN_DB_NODE *dbGlobalArgs, const GWEN_ARGS *argDescrs,
int argc, char **argv);
AQHOME_API int AQH_DataClient_ReadConfigFile(AQH_DATACLIENT *dc);
AQHOME_API int AQH_DataClient_ConnectWithArgs(AQH_DATACLIENT *dc, uint32_t flags);
AQHOME_API GWEN_DB_NODE *AQH_DataClient_GetDbLocalArgs(const AQH_DATACLIENT *dc);

View File

@@ -180,7 +180,7 @@ int AQH_FdObject_Read(AQH_OBJECT *o, uint8_t *ptrBuffer, uint32_t lenBuffer)
return GWEN_ERROR_TRY_AGAIN;
}
else {
DBG_ERROR(AQH_LOGDOMAIN, "Error on read: %s (%d)", strerror(errno), errno);
DBG_INFO(AQH_LOGDOMAIN, "Error on read(%d): %s (%d)", xo->fd, strerror(errno), errno);
close(xo->fd);
xo->fd=-1;
return GWEN_ERROR_IO;

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