Compare commits
342 Commits
mp-2025_10
...
0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d9762c097 | ||
|
|
aed4e0800d | ||
|
|
4411162620 | ||
|
|
0df5c68fb3 | ||
|
|
7fb21a2f06 | ||
|
|
9d4e24222c | ||
|
|
5ad304b988 | ||
|
|
8a9f74b0a1 | ||
|
|
6b0ea55b3d | ||
|
|
ab7e28a9d5 | ||
|
|
d6e731b34e | ||
|
|
a9dabf0eae | ||
|
|
830a25aff7 | ||
|
|
ae2aef8743 | ||
|
|
f555bf7cd6 | ||
|
|
7a58c4a8ad | ||
|
|
0da8170df7 | ||
|
|
c4911a05cc | ||
|
|
fa335392af | ||
|
|
f63eb308b3 | ||
|
|
5fe4b4b705 | ||
|
|
98f1ab779d | ||
|
|
710af679cb | ||
|
|
505910bd12 | ||
|
|
42b7ce1814 | ||
|
|
66be348ecd | ||
|
|
7d556c0f26 | ||
|
|
12cdc6c637 | ||
|
|
b81d798008 | ||
|
|
2a5f09e239 | ||
|
|
9da5f71ce1 | ||
|
|
f0df0219a5 | ||
|
|
87c2314c2d | ||
|
|
5a9bff2d0b | ||
|
|
d7074388e6 | ||
|
|
b00f697582 | ||
|
|
e5dafccb29 | ||
|
|
0e501274eb | ||
|
|
5c2f7496c3 | ||
|
|
1540f62d04 | ||
|
|
cc4696d408 | ||
|
|
6176908c54 | ||
|
|
6ad7bae155 | ||
|
|
487cd13297 | ||
|
|
69daa1465b | ||
|
|
509ec83661 | ||
|
|
95197429ab | ||
|
|
f7a66d6d81 | ||
|
|
47894e6d89 | ||
|
|
4b7c33f43e | ||
|
|
612013f400 | ||
|
|
ca1c5c0cb9 | ||
|
|
95e4de1f33 | ||
|
|
fc020088f4 | ||
|
|
f567630aaa | ||
|
|
28f64a68e6 | ||
|
|
bc79d72077 | ||
|
|
ee74f59563 | ||
|
|
66f061be80 | ||
|
|
82d877d5e3 | ||
|
|
59d1ef7666 | ||
|
|
6ea1e97835 | ||
|
|
a7ddcd2c81 | ||
|
|
aba71f73e0 | ||
|
|
66bc71b2bd | ||
|
|
02a43398a9 | ||
|
|
915e525daf | ||
|
|
34b6bab711 | ||
|
|
c2dcd94c14 | ||
|
|
bd2d5afcb5 | ||
|
|
32a2193074 | ||
|
|
f1d67027fa | ||
|
|
0c810a7842 | ||
|
|
946fdfe39e | ||
|
|
ab9c377a25 | ||
|
|
a0a28aec45 | ||
|
|
d2cbca9d5a | ||
|
|
67be74d2ac | ||
|
|
42874f27cd | ||
|
|
c988876c79 | ||
|
|
c6510f10bf | ||
|
|
e23bfde746 | ||
|
|
07a6c5a540 | ||
|
|
0015a0a06c | ||
|
|
e125c4b027 | ||
|
|
8eed88394f | ||
|
|
b9e40f236e | ||
|
|
68624e92e0 | ||
|
|
41d60f4331 | ||
|
|
a727d600f1 | ||
|
|
e7c195e635 | ||
|
|
30be5b3681 | ||
|
|
bd5a51b4d2 | ||
|
|
1a442cfb21 | ||
|
|
4eac2a105e | ||
|
|
69a16c4c0f | ||
|
|
5e3a8f444a | ||
|
|
722528f4e1 | ||
|
|
2878d3aaa2 | ||
|
|
c5df904fc4 | ||
|
|
6bb26b7c90 | ||
|
|
a0e73d5788 | ||
|
|
d877508e85 | ||
|
|
c2dc819aec | ||
|
|
1f801c41a1 | ||
|
|
21b8c1fe56 | ||
|
|
440f3bcf0e | ||
|
|
3bf327bb2d | ||
|
|
8085043ef6 | ||
|
|
e6aeaabb32 | ||
|
|
70ace036a8 | ||
|
|
1112a1bf6f | ||
|
|
f5c27d7174 | ||
|
|
52b400d9f7 | ||
|
|
fd6616c3c4 | ||
|
|
ab77a71216 | ||
|
|
5c18f5bf9a | ||
|
|
dbb004234f | ||
|
|
646c28e00a | ||
|
|
47f8ef2a62 | ||
|
|
1d39c3c773 | ||
|
|
d688cd3d26 | ||
|
|
1813c9e09e | ||
|
|
274c404732 | ||
|
|
13a3c3a189 | ||
|
|
8671214a2d | ||
|
|
4140e2dc52 | ||
|
|
6a3c8b6cfa | ||
|
|
6d3a27977a | ||
|
|
6c83991df7 | ||
|
|
1d8b467b1d | ||
|
|
a0fc10cccd | ||
|
|
2a76e82923 | ||
|
|
b6f710bd8b | ||
|
|
5119c38ca6 | ||
|
|
393df322f0 | ||
|
|
ce4a4afc68 | ||
|
|
9d92564b2e | ||
|
|
2f570ea110 | ||
|
|
092c667291 | ||
|
|
f6736c345d | ||
|
|
51cedae0cb | ||
|
|
282cd738b4 | ||
|
|
d3f24284ca | ||
|
|
2827f4e063 | ||
|
|
f5e19ac0a1 | ||
|
|
cc7f2dc1db | ||
|
|
e3ae1f9b35 | ||
|
|
e4668378fe | ||
|
|
e24300f16b | ||
|
|
648b0f33c5 | ||
|
|
0758579b43 | ||
|
|
92efebccf1 | ||
|
|
7bd83b32b4 | ||
|
|
3f3cdaac73 | ||
|
|
ef3ed9df12 | ||
|
|
ffde6b0ddb | ||
|
|
f88745c3fd | ||
|
|
283738f4f3 | ||
|
|
2cf4e414d2 | ||
|
|
0af5aed2f6 | ||
|
|
a8cb442502 | ||
|
|
16b820fae1 | ||
|
|
261ddba7c4 | ||
|
|
ae137efb26 | ||
|
|
afdd52eaf6 | ||
|
|
0081c33c00 | ||
|
|
8c59b92100 | ||
|
|
e6e1bf0535 | ||
|
|
fe8681292f | ||
|
|
1edd34fb5a | ||
|
|
f496e6587b | ||
|
|
0d6bbd1147 | ||
|
|
639136431b | ||
|
|
5692e65627 | ||
|
|
baadca7c95 | ||
|
|
43d03b0ace | ||
|
|
d8558015b2 | ||
|
|
1be66e6c7a | ||
|
|
8597f89227 | ||
|
|
d2533f66af | ||
|
|
0b71b9dff7 | ||
|
|
717c9467a8 | ||
|
|
b02ae49484 | ||
|
|
69420d9c7d | ||
|
|
bdd2d3bdc6 | ||
|
|
d6e87aeba5 | ||
|
|
56f2cf6870 | ||
|
|
bf8f584a4a | ||
|
|
b1efa104f0 | ||
|
|
5fc3916457 | ||
|
|
1b0519437c | ||
|
|
9e5fd0bf8c | ||
|
|
e117153622 | ||
|
|
00bea4d9f6 | ||
|
|
f7475b0b31 | ||
|
|
7c37ad930b | ||
|
|
bfd991a768 | ||
|
|
1e8c231707 | ||
|
|
aef7434f9d | ||
|
|
b797c09f67 | ||
|
|
4fec5433b3 | ||
|
|
05ac62ef54 | ||
|
|
409205dcd7 | ||
|
|
599b27c7d2 | ||
|
|
1dd9c8de6e | ||
|
|
36f52a396b | ||
|
|
28deb9c591 | ||
|
|
39d1060334 | ||
|
|
38eb21869e | ||
|
|
a8cdf6ec58 | ||
|
|
40033db235 | ||
|
|
c8e5317cac | ||
|
|
74b4098608 | ||
|
|
7411fd2a13 | ||
|
|
4b4d44d80f | ||
|
|
0fa44cfa4b | ||
|
|
653e63ad34 | ||
|
|
a7cf31b1c1 | ||
|
|
ce240f087e | ||
|
|
3b5d95f8b9 | ||
|
|
c1da37d973 | ||
|
|
eba269f2e7 | ||
|
|
a4b49f8979 | ||
|
|
0df67a3e0b | ||
|
|
fdfc040d19 | ||
|
|
2085ba6bbe | ||
|
|
9a1ca45329 | ||
|
|
026e9179e7 | ||
|
|
c59cdb1470 | ||
|
|
74f4e32767 | ||
|
|
b46c65837f | ||
|
|
8007c8c79a | ||
|
|
eb30e59c13 | ||
|
|
2636fa8b21 | ||
|
|
50eed94821 | ||
|
|
b95c09ad82 | ||
|
|
b4969019b2 | ||
|
|
d963458a91 | ||
|
|
664b8a1ea5 | ||
|
|
be39ad0365 | ||
|
|
41a70a8280 | ||
|
|
e4e7aeb12e | ||
|
|
604734b6e3 | ||
|
|
d64157f7ef | ||
|
|
e5a999e9b6 | ||
|
|
7a4f462fa2 | ||
|
|
a22d774b28 | ||
|
|
faa5991024 | ||
|
|
dbe42c5bcb | ||
|
|
864f815d91 | ||
|
|
94092a82d9 | ||
|
|
242c3061bf | ||
|
|
3225350be5 | ||
|
|
041f0fad6b | ||
|
|
504e7568d4 | ||
|
|
0bcb3b40f7 | ||
|
|
519751043d | ||
|
|
7b4bac534f | ||
|
|
79dde7402f | ||
|
|
b1a002013a | ||
|
|
ebb20150ca | ||
|
|
edc291261f | ||
|
|
715c4f22c4 | ||
|
|
7fce521671 | ||
|
|
065d301292 | ||
|
|
c578ddd1a6 | ||
|
|
13f682b365 | ||
|
|
ff5c49ad7a | ||
|
|
a11c5ac536 | ||
|
|
fb308ba9fc | ||
|
|
b060610f3f | ||
|
|
34176ea2ed | ||
|
|
46afcb3ed1 | ||
|
|
64bc199753 | ||
|
|
73f9273014 | ||
|
|
b1693168c4 | ||
|
|
276fd28782 | ||
|
|
e9f1aec122 | ||
|
|
31cce1a609 | ||
|
|
c2d1234e00 | ||
|
|
0443032de7 | ||
|
|
3009e9d0d0 | ||
|
|
a52d7dd783 | ||
|
|
c2489e1866 | ||
|
|
20189eb1eb | ||
|
|
48d0a3f41f | ||
|
|
21bb33f148 | ||
|
|
0d0b1d2fc6 | ||
|
|
283374b5cd | ||
|
|
0aa4e6846a | ||
|
|
99739325df | ||
|
|
55292bddf1 | ||
|
|
37689fbc1d | ||
|
|
99c58d13e2 | ||
|
|
10c3f3c40d | ||
|
|
edb8e7b859 | ||
|
|
caa2a92722 | ||
|
|
155b9c6f52 | ||
|
|
6468ee18b9 | ||
|
|
5374b6f295 | ||
|
|
00c607a65b | ||
|
|
c3e4435b90 | ||
|
|
8d65dd2182 | ||
|
|
bf1a34b449 | ||
|
|
3038208eab | ||
|
|
636fc026aa | ||
|
|
06f006ee42 | ||
|
|
f9a6e4b1d7 | ||
|
|
9e8a7c338d | ||
|
|
202439b8b6 | ||
|
|
077e5cbedb | ||
|
|
3083b433a3 | ||
|
|
dbba5617c7 | ||
|
|
d8058dc711 | ||
|
|
db6d28b869 | ||
|
|
b520ccb165 | ||
|
|
059654b16f | ||
|
|
5dcb48d671 | ||
|
|
f6e852be74 | ||
|
|
defd869c26 | ||
|
|
ceaf0e0ee5 | ||
|
|
35557d7b22 | ||
|
|
102f4e65e3 | ||
|
|
fbcfd65e7f | ||
|
|
520f371560 | ||
|
|
f8c914538a | ||
|
|
049d651fec | ||
|
|
ece4fe824a | ||
|
|
03f794b253 | ||
|
|
29f74c0eae | ||
|
|
e82c1cbe5c | ||
|
|
9a37bae20d | ||
|
|
783815427a | ||
|
|
9300e515e7 | ||
|
|
660f2502c1 | ||
|
|
5b72686904 | ||
|
|
e58e9b846c | ||
|
|
b4fee78ad8 | ||
|
|
06d287b53d | ||
|
|
7c5abc0f3b | ||
|
|
1b0145c97d |
9
0BUILD
9
0BUILD
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<gwbuild>
|
<gwbuild>
|
||||||
|
|
||||||
<project name="aqhome" version="0.0.19" so_current="0" so_age="0" so_revision="19" write_config_h="TRUE">
|
<project name="aqhome" version="0.3.0" so_current="4" so_age="3" so_revision="0" write_config_h="TRUE">
|
||||||
<setVar name="package">$(project_name)</setVar>
|
<setVar name="package">$(project_name)</setVar>
|
||||||
<setVar name="version">
|
<setVar name="version">
|
||||||
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
|
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
|
||||||
@@ -180,6 +180,7 @@
|
|||||||
devices
|
devices
|
||||||
etc
|
etc
|
||||||
scripts
|
scripts
|
||||||
|
doc
|
||||||
</subdirs>
|
</subdirs>
|
||||||
|
|
||||||
<ifVarMatches name="option_with_avr" value="TRUE" >
|
<ifVarMatches name="option_with_avr" value="TRUE" >
|
||||||
@@ -256,4 +257,10 @@
|
|||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
<extradist>
|
||||||
|
COPYING
|
||||||
|
README.md
|
||||||
|
</extradist>
|
||||||
|
|
||||||
|
|
||||||
</gwbuild>
|
</gwbuild>
|
||||||
|
|||||||
344
COPYING
Normal file
344
COPYING
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
The following is the license text for the GPL in a version that applies to
|
||||||
|
AqHome.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
<https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Moe Ghoul>, 1 April 1989
|
||||||
|
Moe Ghoul, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
82
README.md
Normal file
82
README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
# Aquamaniac Home Control System (AqHomeControl)
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
To compile the project the following software needs to be installed:
|
||||||
|
- gwenhywfar (general purpose library, also containing some development tools)
|
||||||
|
- avrasm (Opensource avr assembler)
|
||||||
|
- gcc (GNU C compiler)
|
||||||
|
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
- download and unpack this project
|
||||||
|
- cd into the main folder
|
||||||
|
- create a build folder (e.g. "mkdir build")
|
||||||
|
- cd into that "build" folder
|
||||||
|
- configure package for building ("gwbuild -s ../")
|
||||||
|
- build autogenerated files ("gwbuild -p" and "gwbuild -Btm2builder")
|
||||||
|
- build project ("gwbuild -j NUM_OF_CPUS")
|
||||||
|
- install project ("sudo gwbuild -i")
|
||||||
|
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
See https://gitea.aqbanking.de/martin/aqhomecontrol/wiki for more information (in German).
|
||||||
|
|
||||||
|
The Aquamaniac Home automation/control system consists of multiple parts:
|
||||||
|
- nodes
|
||||||
|
- small PCBs with AVR microcontrollers, for now:
|
||||||
|
- AtTiny84
|
||||||
|
- AtTiny85
|
||||||
|
- AtTiny841
|
||||||
|
- AtMega644
|
||||||
|
- examples:
|
||||||
|
- [N28](https://gitea.aqbanking.de/martin/aqhomecontrol/src/branch/master/doc/mcu-examples/n28/)
|
||||||
|
- [N29](https://gitea.aqbanking.de/martin/aqhomecontrol/src/branch/master/doc/mcu-examples/n29/)
|
||||||
|
- [N30](https://gitea.aqbanking.de/martin/aqhomecontrol/src/branch/master/doc/mcu-examples/n30/)
|
||||||
|
- decentralized, nodes just broadcast measured data over a two-wire
|
||||||
|
communication network (clock/data), addresses are auto-assigned, no prior
|
||||||
|
setup necessary, no central bottleneck or single point of failure
|
||||||
|
- node types
|
||||||
|
- environmental measuring (temperature, humidity, CO2 and others)
|
||||||
|
- motion detection, door/window sensors
|
||||||
|
- LED strip controllers
|
||||||
|
- modular operating system written in AVR assembler
|
||||||
|
- event-driven GUI for ATmega MCUs (work in progress)
|
||||||
|
- driver modules for busses:
|
||||||
|
- one-wire bus
|
||||||
|
- two-wire-bus
|
||||||
|
- SPI bus
|
||||||
|
- UART
|
||||||
|
- driver modules for devices/sensors (incomplete list):
|
||||||
|
- DS18b20 sensors for temperature measurement
|
||||||
|
- SI7021 sensors for temperature and humidity measurement
|
||||||
|
- SGP_30 sensors for air quality measurement
|
||||||
|
- ccs811 sensors for air quality measurement
|
||||||
|
- SK6812 LED driver
|
||||||
|
- ds3231 realtime clock module
|
||||||
|
- I2C displays (work-in-progress)
|
||||||
|
- SPI displays (work-in-progress)
|
||||||
|
|
||||||
|
- PC applications
|
||||||
|
- aqhome-data
|
||||||
|
- TCP data service receiving sensor data
|
||||||
|
- aqhome-nodes
|
||||||
|
- service receiving sensor data via inter-node bus from nodes
|
||||||
|
and forwarding that data to aqhome-data
|
||||||
|
- aqhome-mqttlog
|
||||||
|
- service exchanging data between aqhome-data service and a
|
||||||
|
mqtt server
|
||||||
|
- aqhome-react
|
||||||
|
- service reacting to data received by aqhome-data (home automation service)
|
||||||
|
- aqhome-tool
|
||||||
|
- tool to manipulate aqhome-data (e.g. edit devices and values,
|
||||||
|
add data points, create graphs from aqhome-data)
|
||||||
|
- tool to flash firmware for nodes, list nodes
|
||||||
|
- aqhome-cgi
|
||||||
|
- HTTP service for user interaction with aqhome-data
|
||||||
|
|
||||||
@@ -17,6 +17,14 @@
|
|||||||
|
|
||||||
#include <gwenhywfar/debug.h>
|
#include <gwenhywfar/debug.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -680,5 +688,133 @@ AQH_USER_LIST *AQH_ModService_LoadRawUsers(AQH_MODULE *m)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
while (*src) {
|
||||||
|
unsigned char x;
|
||||||
|
|
||||||
|
x=(unsigned char)*src;
|
||||||
|
if (!(
|
||||||
|
(x>='A' && x<='Z') ||
|
||||||
|
(x>='a' && x<='z') ||
|
||||||
|
(x>='0' && x<='9') ||
|
||||||
|
NULL!=strchr("-_", x)
|
||||||
|
)) {
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
GWEN_Buffer_AppendByte(buf, '%');
|
||||||
|
c=(((unsigned char)(*src))>>4)&0xf;
|
||||||
|
if (c>9)
|
||||||
|
c+=7;
|
||||||
|
c+='0';
|
||||||
|
GWEN_Buffer_AppendByte(buf, c);
|
||||||
|
c=((unsigned char)(*src))&0xf;
|
||||||
|
if (c>9)
|
||||||
|
c+=7;
|
||||||
|
c+='0';
|
||||||
|
GWEN_Buffer_AppendByte(buf, c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
GWEN_Buffer_AppendByte(buf, *src);
|
||||||
|
|
||||||
|
src++;
|
||||||
|
} /* while */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
while (*src) {
|
||||||
|
int charHandled=0;
|
||||||
|
|
||||||
|
if (*src=='%') {
|
||||||
|
if (strlen(src)>2) {
|
||||||
|
unsigned char d1, d2;
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
if (isxdigit((int)src[1]) && isxdigit((int)src[2])) {
|
||||||
|
/* skip '%' */
|
||||||
|
src++;
|
||||||
|
/* read first digit */
|
||||||
|
d1=(unsigned char)(toupper(*src));
|
||||||
|
|
||||||
|
/* get second digit */
|
||||||
|
src++;
|
||||||
|
d2=(unsigned char)(toupper(*src));
|
||||||
|
/* compute character */
|
||||||
|
d1-='0';
|
||||||
|
if (d1>9)
|
||||||
|
d1-=7;
|
||||||
|
c=(d1<<4)&0xf0;
|
||||||
|
d2-='0';
|
||||||
|
if (d2>9)
|
||||||
|
d2-=7;
|
||||||
|
c+=(d2&0xf);
|
||||||
|
/* store character */
|
||||||
|
GWEN_Buffer_AppendByte(buf, (char)c);
|
||||||
|
charHandled=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!charHandled)
|
||||||
|
GWEN_Buffer_AppendByte(buf, *src);
|
||||||
|
src++;
|
||||||
|
} /* while */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
time_t t1;
|
||||||
|
|
||||||
|
if (lstat(sPath, &sb)==-1) {
|
||||||
|
DBG_ERROR(NULL, "Error on lstat(%s): %s (%d)", sPath, strerror(errno), errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
t1=time(0);
|
||||||
|
if ((t1-sb.st_mtime)<(time_t) seconds) {
|
||||||
|
DBG_DEBUG(NULL, "File %s is current", sPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *ibuf;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ibuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
||||||
|
/* read file */
|
||||||
|
rv=GWEN_SyncIo_Helper_ReadFile(sFilename, ibuf);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_ERROR(NULL, "Error reading \"%s\" (%d)", sFilename, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GWEN_Buffer_Reset(dbuf);
|
||||||
|
GWEN_Buffer_AppendBytes(dbuf, GWEN_Buffer_GetStart(ibuf), GWEN_Buffer_GetUsedBytes(ibuf));
|
||||||
|
|
||||||
|
if (sMimeType && *sMimeType) {
|
||||||
|
GWEN_BUFFER *tbuf;
|
||||||
|
|
||||||
|
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAA(tbuf, "Content-type: %s", sMimeType);
|
||||||
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(tbuf));
|
||||||
|
GWEN_Buffer_free(tbuf);
|
||||||
|
}
|
||||||
|
AQCGI_Request_AddFlags(rq, AQH_MODSERVICE_RQFLAGS_RAWFILE);
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(ibuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODU
|
|||||||
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn);
|
void AQH_ModService_SetAddHeaderFn(AQH_MODULE *m, AQH_MODSERVICE_ADDHEADER_FN fn);
|
||||||
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn);
|
void AQH_ModService_SetAddFooterFn(AQH_MODULE *m, AQH_MODSERVICE_ADDFOOTER_FN fn);
|
||||||
|
|
||||||
|
void AQH_ModService_EscapeToBuffer(const char *src, GWEN_BUFFER *buf);
|
||||||
|
void AQH_ModService_UnescapeToBuffer(const char *src, GWEN_BUFFER *buf);
|
||||||
|
|
||||||
|
int AQH_ModService_FileIsCurrent(const char *sPath, int seconds);
|
||||||
|
|
||||||
|
int AQH_ModService_RespondWithMimeFile(AQCGI_REQUEST *rq, const char *sFilename, const char *sMimeType, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
mdevices_vgraph.h
|
mdevices_vgraph.h
|
||||||
mdevices_device.h
|
mdevices_device.h
|
||||||
mdevices_setdevice.h
|
mdevices_setdevice.h
|
||||||
|
mdevices_page.h
|
||||||
</headers>
|
</headers>
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@
|
|||||||
mdevices_vgraph.c
|
mdevices_vgraph.c
|
||||||
mdevices_device.c
|
mdevices_device.c
|
||||||
mdevices_setdevice.c
|
mdevices_setdevice.c
|
||||||
|
mdevices_page.c
|
||||||
</sources>
|
</sources>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "aqhome-cgi/modules/devices/mdevices_vgraph.h"
|
#include "aqhome-cgi/modules/devices/mdevices_vgraph.h"
|
||||||
#include "aqhome-cgi/modules/devices/mdevices_device.h"
|
#include "aqhome-cgi/modules/devices/mdevices_device.h"
|
||||||
#include "aqhome-cgi/modules/devices/mdevices_setdevice.h"
|
#include "aqhome-cgi/modules/devices/mdevices_setdevice.h"
|
||||||
|
#include "aqhome-cgi/modules/devices/mdevices_page.h"
|
||||||
#include "aqhome-cgi/service/module.h"
|
#include "aqhome-cgi/service/module.h"
|
||||||
#include "aqhome-cgi/modules/mdataclient.h"
|
#include "aqhome-cgi/modules/mdataclient.h"
|
||||||
|
|
||||||
@@ -61,6 +62,11 @@ static void _handleRqSetDataPost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *
|
|||||||
static void _handleRqGraphGet(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 _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 _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
static void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
static AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues);
|
||||||
|
|
||||||
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
static void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
||||||
static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
static void _addLastValueToForm(AQH_DATACLIENT *dc, const AQH_VALUE *value, GWEN_BUFFER *dbuf);
|
||||||
@@ -81,6 +87,9 @@ static AQH_MODSERVICE_HANDLER_ENTRY _requestTable[]={
|
|||||||
{"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet},
|
{"value.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqValueGet},
|
||||||
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
|
{"setdata.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqSetDataPost},
|
||||||
{"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet},
|
{"graph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqGraphGet},
|
||||||
|
{"page.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGet},
|
||||||
|
{"page.html", AQCGI_REQUEST_METHOD_POST, P_VALUEWRITE, _handleRqPagePost},
|
||||||
|
{"pgraph.html", AQCGI_REQUEST_METHOD_GET, P_DEVICEREAD | P_VALUEREAD, _handleRqPageGraphGet},
|
||||||
{NULL, 0, 0, NULL}
|
{NULL, 0, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,6 +180,26 @@ void _handleRqDevicePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGet, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPagePost, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handleRqPageGraphGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
AQH_ModDataClient_HandleRequest(m, rq, session, AQH_ModDevices_RunPageGraph, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -227,6 +256,44 @@ uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w)
|
||||||
|
{
|
||||||
|
uint32_t colorOut;
|
||||||
|
|
||||||
|
colorOut=((r & 0xff)<<16) | ((g & 0xff)<<24) | (b & 0xff) | ((w & 0xff)<<8);
|
||||||
|
return colorOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetR(uint32_t color)
|
||||||
|
{ /* GGRRWWBB */
|
||||||
|
return (color & 0x00ff0000)>>16;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetG(uint32_t color)
|
||||||
|
{ /* GGRRWWBB */
|
||||||
|
return (color & 0xff000000)>>24;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetB(uint32_t color)
|
||||||
|
{ /* GGRRWWBB */
|
||||||
|
return (color & 0x000000ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetW(uint32_t color)
|
||||||
|
{ /* GGRRWWBB */
|
||||||
|
return (color & 0x0000ff00)>>8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
|
void _addValueActionToForm(const AQH_VALUE *value, GWEN_BUFFER *dbuf)
|
||||||
{
|
{
|
||||||
const char *sValueName;
|
const char *sValueName;
|
||||||
@@ -392,4 +459,107 @@ AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue)
|
||||||
|
{
|
||||||
|
const char *sValueSystemName;
|
||||||
|
uint64_t dataPoints[2];
|
||||||
|
uint64_t recvdNum;
|
||||||
|
// uint64_t timestamp;
|
||||||
|
union {double f; uint64_t i;} u;
|
||||||
|
int intVal;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
|
||||||
|
if (recvdNum>0) {
|
||||||
|
// timestamp=dataPoints[0];
|
||||||
|
u.i=dataPoints[1];
|
||||||
|
intVal=(int) u.f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName);
|
||||||
|
intVal=defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t AQH_ModDevices_ValueGetLastDataAsUint32(AQH_DATACLIENT *dc, const AQH_VALUE *value, uint32_t defaultValue)
|
||||||
|
{
|
||||||
|
const char *sValueSystemName;
|
||||||
|
uint64_t dataPoints[2];
|
||||||
|
uint64_t recvdNum;
|
||||||
|
// uint64_t timestamp;
|
||||||
|
union {double f; uint64_t i;} u;
|
||||||
|
uint32_t uintVal;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
recvdNum=AQH_DataClient_GetLastData(dc, sValueSystemName, &dataPoints[0], 1);
|
||||||
|
if (recvdNum>0) {
|
||||||
|
// timestamp=dataPoints[0];
|
||||||
|
u.i=dataPoints[1];
|
||||||
|
uintVal=(uint32_t) u.f;
|
||||||
|
DBG_ERROR(NULL, "Transformed value %.2f -> %08x", u.f, uintVal);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "No last value for \"%s\"", sValueSystemName);
|
||||||
|
uintVal=defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uintVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
|
||||||
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
|
||||||
|
{
|
||||||
|
uint64_t *dataPoints;
|
||||||
|
uint64_t recvdNum;
|
||||||
|
|
||||||
|
dataPoints=malloc(num*sizeof(uint64_t)*2);
|
||||||
|
|
||||||
|
recvdNum=AQH_DataClient_GetPeriodData(dc, systemValueName, dataPoints, num, tsBegin, tsEnd);
|
||||||
|
if (recvdNum>0) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
|
||||||
|
dpList=_createDataPairListFromDataPoints(dataPoints, recvdNum);
|
||||||
|
free(dataPoints);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "No data received for %s", systemValueName);
|
||||||
|
free(dataPoints);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *_createDataPairListFromDataPoints(const uint64_t *dataPoints, uint64_t numValues)
|
||||||
|
{
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
uint64_t i;
|
||||||
|
|
||||||
|
DBG_DEBUG(NULL, "Got %d datapoints", (int) numValues);
|
||||||
|
dpList=AQDG_Graph_DataPair_List_new();
|
||||||
|
for(i=0; i<numValues; i++) {
|
||||||
|
AQDG_GRAPH_DATAPAIR *dp;
|
||||||
|
double timestamp;
|
||||||
|
union {double f; uint64_t i;} u;
|
||||||
|
|
||||||
|
timestamp=(double)(*(dataPoints++));
|
||||||
|
u.i=*(dataPoints++);
|
||||||
|
dp=AQDG_Graph_DataPair_new();
|
||||||
|
AQDG_Graph_DataPair_SetValueX(dp, timestamp);
|
||||||
|
AQDG_Graph_DataPair_SetValueY(dp, u.f);
|
||||||
|
AQDG_Graph_DataPair_List_Add(dp, dpList);
|
||||||
|
}
|
||||||
|
AQDG_Graph_DataPair_List_SortByValueX(dpList, 1);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <aqcgi/request.h>
|
#include <aqcgi/request.h>
|
||||||
|
|
||||||
|
#include <aqdiagram/graph/datapair.h>
|
||||||
|
|
||||||
#include <gwenhywfar/buffer.h>
|
#include <gwenhywfar/buffer.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +46,23 @@ int AQH_ModDevices_Create(AQH_SERVICE *sv);
|
|||||||
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
|
uint32_t AQH_ModDevices_ColorFromHexString(const char *s);
|
||||||
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
|
uint32_t AQH_ModDevices_HtmlColorToValueRGBW(uint32_t colorIn);
|
||||||
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn);
|
uint32_t AQH_ModDevices_RgbwToHtmlColor(uint32_t colorIn);
|
||||||
|
uint32_t AQH_ModDevices_RgbwFromComponents(int r, int g, int b, int w);
|
||||||
|
|
||||||
|
int AQH_ModDevices_RgbwGetR(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetG(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetB(uint32_t color);
|
||||||
|
int AQH_ModDevices_RgbwGetW(uint32_t color);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName);
|
AQH_VALUE *AQH_ModDevices_GetValueForDevice(AQH_DATACLIENT *dc, const char *sDeviceName, const char *sValueName);
|
||||||
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName);
|
AQH_DEVICE *AQH_ModDevices_GetDevice(AQH_DATACLIENT *dc, const char *sDeviceName);
|
||||||
|
int AQH_ModDevices_ValueGetLastDataAsInt(AQH_DATACLIENT *dc, const AQH_VALUE *value, int defaultValue);
|
||||||
|
uint32_t AQH_ModDevices_ValueGetLastDataAsUint32(AQH_DATACLIENT *dc, const AQH_VALUE *value, uint32_t defaultValue);
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *AQH_ModDevices_RequestDataPairList(AQH_DATACLIENT *dc, const char *systemValueName,
|
||||||
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
961
apps/aqhome-cgi/modules/devices/mdevices_page.c
Normal file
961
apps/aqhome-cgi/modules/devices/mdevices_page.c
Normal file
@@ -0,0 +1,961 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "./mdevices_page.h"
|
||||||
|
|
||||||
|
#include "aqhome-cgi/service/module.h"
|
||||||
|
#include "aqhome-cgi/modules/mdataclient.h"
|
||||||
|
|
||||||
|
#include <aqdiagram/graph/timegraph.h>
|
||||||
|
#include <aqdiagram/graph/w_graph.h>
|
||||||
|
#include <aqdiagram/draw/context_cairo.h>
|
||||||
|
|
||||||
|
#include <gwenhywfar/debug.h>
|
||||||
|
#include <gwenhywfar/timestamp.h>
|
||||||
|
#include <gwenhywfar/text.h>
|
||||||
|
#include <gwenhywfar/directory.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* defs and enums
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define GBAS GWEN_Buffer_AppendString
|
||||||
|
#define GBAA GWEN_Buffer_AppendArgs
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MY_LAYOUT_NONE=0,
|
||||||
|
MY_LAYOUT_HORIZONTAL,
|
||||||
|
MY_LAYOUT_VERTICAL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* forward declarations
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
static void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor);
|
||||||
|
static void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf);
|
||||||
|
static void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, const char *sFilename, GWEN_XMLNODE *nGraph);
|
||||||
|
static void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd);
|
||||||
|
static AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd);
|
||||||
|
static uint64_t _parseTime(const char *s);
|
||||||
|
static GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName);
|
||||||
|
static void _mkPathForGraph(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *dbuf);
|
||||||
|
static void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
|
||||||
|
static void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf);
|
||||||
|
static void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value);
|
||||||
|
static int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue);
|
||||||
|
static void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
|
||||||
|
static void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue);
|
||||||
|
|
||||||
|
static void _sendPageList(AQH_MODULE *m, GWEN_BUFFER *dbuf);
|
||||||
|
static GWEN_STRINGLIST *_listPageFiles(AQH_MODULE *m);
|
||||||
|
static GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName);
|
||||||
|
static GWEN_XMLNODE *_readPageFile(const char *sFilename);
|
||||||
|
static int _layoutFromString(const char *s);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* code
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbQuery;
|
||||||
|
const char *sPageId;
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "RunPageGet");
|
||||||
|
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
||||||
|
sPageId=dbQuery?GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL):NULL;
|
||||||
|
if (sPageId && *sPageId) {
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
fileNode=_readPage(m, sPageId);
|
||||||
|
if (fileNode) {
|
||||||
|
GWEN_XMLNODE *nPage;
|
||||||
|
|
||||||
|
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", NULL, NULL);
|
||||||
|
if (nPage) {
|
||||||
|
_writePage(m, dc, nPage, dbuf);
|
||||||
|
AQCGI_Request_AddResponseHeaderData(rq, "Refresh: 120");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "No page element in file for \"%s\"", sPageId);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Reading page list");
|
||||||
|
_sendPageList(m, dbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbQuery;
|
||||||
|
const char *sPageId;
|
||||||
|
const char *sGraphId;
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "RunPageGraphGet");
|
||||||
|
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
||||||
|
sPageId=GWEN_DB_GetCharValue(dbQuery, "page", 0, NULL);
|
||||||
|
sGraphId=GWEN_DB_GetCharValue(dbQuery, "graph", 0, NULL);
|
||||||
|
if (sPageId && *sPageId && sGraphId && *sGraphId) {
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
fileNode=_readPage(m, sPageId);
|
||||||
|
if (fileNode) {
|
||||||
|
GWEN_XMLNODE *nPage;
|
||||||
|
GWEN_XMLNODE *nGraph;
|
||||||
|
|
||||||
|
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId);
|
||||||
|
nGraph=_getSubItemNode(nPage, sGraphId, "graph");
|
||||||
|
if (nPage && nGraph)
|
||||||
|
_handlePageGraph(m, rq, dc, sGraphId, nGraph, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Graph %s/%s not found", sPageId, sGraphId);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbPost;
|
||||||
|
const char *sPageId;
|
||||||
|
const char *sActorId;
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "RunPagePost");
|
||||||
|
dbPost=AQCGI_Request_GetDbPostBody(rq);
|
||||||
|
sPageId=GWEN_DB_GetCharValue(dbPost, "page", 0, NULL);
|
||||||
|
sActorId=GWEN_DB_GetCharValue(dbPost, "actor", 0, NULL);
|
||||||
|
if (sPageId && *sPageId && sActorId && *sActorId) {
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
fileNode=_readPage(m, sPageId);
|
||||||
|
if (fileNode) {
|
||||||
|
GWEN_XMLNODE *nPage;
|
||||||
|
GWEN_XMLNODE *nActor;
|
||||||
|
|
||||||
|
nPage=GWEN_XMLNode_FindFirstTag(fileNode, "page", "id", sPageId);
|
||||||
|
nActor=_getSubItemNode(nPage, sActorId, "actor");
|
||||||
|
if (nPage && nActor) {
|
||||||
|
_handlePageActor(rq, dc, sActorId, nActor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Actor %s/%s not found", sPageId, sActorId);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sPageId && *sPageId) {
|
||||||
|
GWEN_BUFFER *pbuf;
|
||||||
|
|
||||||
|
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAS(pbuf, "Location: page.html?page=");
|
||||||
|
GWEN_Text_EscapeToBuffer(sPageId, pbuf);
|
||||||
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
||||||
|
GWEN_Buffer_free(pbuf);
|
||||||
|
}
|
||||||
|
AQCGI_Request_SetResponseCode(rq, 303);
|
||||||
|
AQCGI_Request_SetResponseText(rq, "See other");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handlePageActor(AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sActorId, GWEN_XMLNODE *nActor)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbPost;
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
|
||||||
|
dbPost=AQCGI_Request_GetDbPostBody(rq);
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(nActor, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(nActor, "value", NULL);
|
||||||
|
|
||||||
|
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQH_VALUE *value;
|
||||||
|
|
||||||
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
|
if (value) {
|
||||||
|
const char *sSystemValueName;
|
||||||
|
|
||||||
|
sSystemValueName=AQH_Value_GetNameForSystem(value);
|
||||||
|
if (sSystemValueName) {
|
||||||
|
const char *sData;
|
||||||
|
|
||||||
|
sData=GWEN_DB_GetCharValue(dbPost, sActorId, 0, NULL);
|
||||||
|
DBG_INFO(NULL, "Setting value %s to %s", sSystemValueName, sData?sData:"empty/no value");
|
||||||
|
switch(AQH_Value_GetModality(value)) {
|
||||||
|
case AQH_ValueModality_RGBW: _setRgbwData(dc, dbPost, sActorId, value); break;
|
||||||
|
case AQH_ValueModality_OnOff: _setOnOffData(dc, value, sData); break;
|
||||||
|
case AQH_ValueModality_OnOffAuto: _setOnOffAutoData(dc, value, sData); break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _handlePageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_DATACLIENT *dc, const char *sGraphId, GWEN_XMLNODE *nGraph, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *fbuf;
|
||||||
|
int refreshTime;
|
||||||
|
|
||||||
|
refreshTime=GWEN_XMLNode_GetIntProperty(nGraph, "refreshTime", 120);
|
||||||
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
_mkPathForGraph(m, sGraphId, fbuf);
|
||||||
|
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), refreshTime)) {
|
||||||
|
_genPageGraph(dc, sGraphId, GWEN_Buffer_GetStart(fbuf), nGraph);
|
||||||
|
}
|
||||||
|
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _genPageGraph(AQH_DATACLIENT *dc, const char *sGraphId, const char *sFilename, GWEN_XMLNODE *nGraph)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
const char *sTitle;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
int precision;
|
||||||
|
uint64_t tsBegin;
|
||||||
|
uint64_t tsEnd;
|
||||||
|
AQDG_GRAPH *g;
|
||||||
|
AQDG_DRAW_CONTEXT *drawContext;
|
||||||
|
AQDG_OBJECT *graphObject;
|
||||||
|
uint32_t tickFlags=0;
|
||||||
|
double upperLimit;
|
||||||
|
double lowerLimit;
|
||||||
|
|
||||||
|
sTitle=GWEN_XMLNode_GetProperty(nGraph, "title", "untitled");
|
||||||
|
w=GWEN_XMLNode_GetIntProperty(nGraph, "width", AQH_MODDEVICES_GRAPH_WIDTH);
|
||||||
|
h=GWEN_XMLNode_GetIntProperty(nGraph, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
|
||||||
|
precision=GWEN_XMLNode_GetIntProperty(nGraph, "precision", 2);
|
||||||
|
tsBegin=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "begin", "-4h"));
|
||||||
|
tsEnd=_parseTime(GWEN_XMLNode_GetProperty(nGraph, "end", "0"));
|
||||||
|
|
||||||
|
s=GWEN_XMLNode_GetProperty(nGraph, "lowerLimit", NULL);
|
||||||
|
if (s && *s) {
|
||||||
|
if (1==sscanf(s, "%lf", &lowerLimit))
|
||||||
|
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MINY;
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Ignoring invalid lowerLimit (%s)", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s=GWEN_XMLNode_GetProperty(nGraph, "upperLimit", NULL);
|
||||||
|
if (s && *s) {
|
||||||
|
if (1==sscanf(s, "%lf", &upperLimit))
|
||||||
|
tickFlags|=AQDG_TIMEGRAPH_SETUPTICKS_FLAGS_MAXY;
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Ignoring invalid upperLimit (%s)", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g=AQDG_TimeGraph_new(sTitle, NULL, "Value", NULL, precision);
|
||||||
|
_addCurves(dc, g, nGraph, tsBegin, tsEnd);
|
||||||
|
AQDG_TimeGraph_SetupTicks(g, tickFlags, lowerLimit, upperLimit);
|
||||||
|
|
||||||
|
DBG_DEBUG(NULL, "Draw graph for %s", sGraphId);
|
||||||
|
drawContext=AQDG_Draw_ContextCairo_Png_new(sFilename, w, h);
|
||||||
|
graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
|
||||||
|
AQDG_Object_SetWidth(graphObject, w);
|
||||||
|
AQDG_Object_SetHeight(graphObject, h);
|
||||||
|
|
||||||
|
AQDG_GraphWidget_SetupDefaultPens(graphObject);
|
||||||
|
AQDG_GraphWidget_SetupDefaultFonts(graphObject);
|
||||||
|
|
||||||
|
AQDG_GraphWidget_FinishWithGraph(graphObject, g);
|
||||||
|
|
||||||
|
AQDG_Object_free(graphObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _addCurves(AQH_DATACLIENT *dc, AQDG_GRAPH *g, GWEN_XMLNODE *nGraph, uint64_t tsBegin, uint64_t tsEnd)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *nCurve;
|
||||||
|
|
||||||
|
nCurve=GWEN_XMLNode_FindFirstTag(nGraph, "curve", NULL, NULL);
|
||||||
|
while(nCurve) {
|
||||||
|
const char *sModifier;
|
||||||
|
|
||||||
|
sModifier=GWEN_XMLNode_GetProperty(nCurve, "modifier", NULL);
|
||||||
|
if (sModifier && *sModifier) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
|
||||||
|
dpList=_readCurveData(dc, nCurve, tsBegin, tsEnd);
|
||||||
|
if (dpList) {
|
||||||
|
const char *sCurveLabel;
|
||||||
|
|
||||||
|
sCurveLabel=GWEN_XMLNode_GetProperty(nCurve, "title", NULL);
|
||||||
|
DBG_DEBUG(NULL, "Adding data for %s", sCurveLabel?sCurveLabel:"<no title>");
|
||||||
|
AQDG_TimeGraph_ModifyDataAndAddCurve(g, sCurveLabel?sCurveLabel:"<no title>", sModifier, dpList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nCurve=GWEN_XMLNode_FindNextTag(nCurve, "curve", NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *_readCurveData(AQH_DATACLIENT *dc, GWEN_XMLNODE *nCurve, uint64_t tsBegin, uint64_t tsEnd)
|
||||||
|
{
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(nCurve, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(nCurve, "value", NULL);
|
||||||
|
|
||||||
|
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
||||||
|
GWEN_BUFFER *vbuf;
|
||||||
|
|
||||||
|
vbuf=GWEN_Buffer_new(0, 64, 0, 1);
|
||||||
|
GBAA(vbuf, "%s/%s", sDeviceName, sValueName);
|
||||||
|
dpList=AQH_ModDevices_RequestDataPairList(dc, GWEN_Buffer_GetStart(vbuf), tsBegin, tsEnd, 100000);
|
||||||
|
if (dpList) {
|
||||||
|
GWEN_Buffer_free(vbuf);
|
||||||
|
return dpList;
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(vbuf);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move to aqhome.{c,h}
|
||||||
|
uint64_t _parseTime(const char *s)
|
||||||
|
{
|
||||||
|
if (s && *s) {
|
||||||
|
if (*s=='-') {
|
||||||
|
uint64_t x=0;
|
||||||
|
uint64_t now=time(NULL);
|
||||||
|
|
||||||
|
s++;
|
||||||
|
while(*s && isdigit(*s)) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
i=*(s++)-'0';
|
||||||
|
x*=10;
|
||||||
|
x+=i;
|
||||||
|
}
|
||||||
|
if (*s) {
|
||||||
|
switch(*s) {
|
||||||
|
case 0:
|
||||||
|
case 'm': x*=60; break;
|
||||||
|
case 'h': x*=(60*60); break;
|
||||||
|
case 'd': x*=(60*60*24); break;
|
||||||
|
case 'w': x*=(60*60*24*7); break;
|
||||||
|
case 'M': x*=(60*60*24*30); break;
|
||||||
|
case 'y': x*=(60*60*24*365); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (now-x);
|
||||||
|
}
|
||||||
|
if (*s=='@') {
|
||||||
|
int y, m, d, H, M, S;
|
||||||
|
|
||||||
|
if (6==sscanf(s+1, "%d/%d/%d-%d:%d:%d", &y, &m, &d, &H, &M, &S)) {
|
||||||
|
GWEN_TIMESTAMP *ts;
|
||||||
|
uint64_t x=0;
|
||||||
|
|
||||||
|
ts=GWEN_Timestamp_new(y, m, d, H, M, S);
|
||||||
|
x=GWEN_Timestamp_toTimeT(ts);
|
||||||
|
GWEN_Timestamp_free(ts);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Invalid timespec [%s], expected: @YYYY/MM/DD-HH:MM:SS", s);
|
||||||
|
return (uint64_t) (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned long int x;
|
||||||
|
|
||||||
|
if (1!=sscanf(s, "%lu", &x)) {
|
||||||
|
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
|
||||||
|
return (uint64_t) (-1);
|
||||||
|
}
|
||||||
|
return (uint64_t) x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_XMLNODE *_getSubItemNode(GWEN_XMLNODE *nPage, const char *sId, const char *sElementName)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *nItem;
|
||||||
|
|
||||||
|
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
|
||||||
|
while(nItem) {
|
||||||
|
GWEN_XMLNODE *nGraph;
|
||||||
|
|
||||||
|
nGraph=GWEN_XMLNode_FindFirstTag(nItem, sElementName, "id", sId);
|
||||||
|
if (nGraph)
|
||||||
|
return nGraph;
|
||||||
|
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _mkPathForGraph(AQH_MODULE *m, const char *sGraphId, GWEN_BUFFER *buf)
|
||||||
|
{
|
||||||
|
AQH_SERVICE *sv;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
sv=AQH_ModService_GetService(m);
|
||||||
|
s=AQH_Service_GetCacheFolder(sv);
|
||||||
|
GBAA(buf, "%s%s%s", s, GWEN_DIR_SEPARATOR_S, sGraphId);
|
||||||
|
AQH_ModService_EscapeToBuffer(s, buf);
|
||||||
|
GBAS(buf, ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writePage(AQH_MODULE *m, AQH_DATACLIENT *dc, GWEN_XMLNODE *nPage, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sPageId;
|
||||||
|
GWEN_XMLNODE *nItem;
|
||||||
|
int layout;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
sPageId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
|
||||||
|
|
||||||
|
/* title */
|
||||||
|
s=GWEN_XMLNode_GetProperty(nPage, "title", NULL);
|
||||||
|
if (s && *s)
|
||||||
|
GBAA(dbuf, "<h1>%s</h1>\n", s);
|
||||||
|
|
||||||
|
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nPage, "layout", "none"));
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<table class=\"pageTable\">\n");
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
nItem=GWEN_XMLNode_FindFirstTag(nPage, "item", NULL, NULL);
|
||||||
|
while(nItem) {
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td>");
|
||||||
|
_writeItem(m, dc, sPageId, nItem, dbuf);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>");
|
||||||
|
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
nItem=GWEN_XMLNode_FindNextTag(nItem, "item", NULL, NULL);
|
||||||
|
} /* while */
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</table> <!-- pageTable -->\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeItem(AQH_MODULE *m, AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *nItem, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *n;
|
||||||
|
uint32_t perms;
|
||||||
|
int layout;
|
||||||
|
|
||||||
|
perms=AQH_ModService_GetUserPerms(m);
|
||||||
|
layout=_layoutFromString(GWEN_XMLNode_GetProperty(nItem, "layout", "none"));
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<table class=\"itemTable\">\n");
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
n=GWEN_XMLNode_GetFirstTag(nItem);
|
||||||
|
while(n) {
|
||||||
|
const char *sName;
|
||||||
|
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "<tr>\n");
|
||||||
|
sName=GWEN_XMLNode_GetData(n);
|
||||||
|
if (sName && *sName) {
|
||||||
|
if (strcasecmp(sName, "actor")==0) {
|
||||||
|
if (perms && AQH_MODDEVICES_PERMS_VALUEWRITE)
|
||||||
|
_writeActor(dc, sPageId, n, layout, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "No permissions to write values");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sName, "graph")==0)
|
||||||
|
_writeGraph(sPageId, n, layout, dbuf);
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Ignoring element \"%s\"", sName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layout==MY_LAYOUT_VERTICAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
n=GWEN_XMLNode_GetNextTag(n);
|
||||||
|
}
|
||||||
|
if (layout==MY_LAYOUT_HORIZONTAL)
|
||||||
|
GBAS(dbuf, "</tr>\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</table> <!-- itemTable -->\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeActor(AQH_DATACLIENT *dc, const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sActorId;
|
||||||
|
const char *sDeviceName;
|
||||||
|
const char *sValueName;
|
||||||
|
const char *sLabel;
|
||||||
|
|
||||||
|
sActorId=GWEN_XMLNode_GetProperty(n, "id", NULL);
|
||||||
|
sLabel=GWEN_XMLNode_GetProperty(n, "label", NULL);
|
||||||
|
sDeviceName=GWEN_XMLNode_GetProperty(n, "device", NULL);
|
||||||
|
sValueName=GWEN_XMLNode_GetProperty(n, "value", NULL);
|
||||||
|
if (sActorId && *sActorId && sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
||||||
|
AQH_VALUE *value;
|
||||||
|
|
||||||
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
|
if (value) {
|
||||||
|
uint32_t lastData;
|
||||||
|
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
lastData=AQH_ModDevices_ValueGetLastDataAsUint32(dc, value, 0);
|
||||||
|
GBAS(dbuf,"<form action=\"page.html\" method=\"post\">\n");
|
||||||
|
GBAA(dbuf, "<input type=\"hidden\" name=\"page\" value=\"%s\">\n", sPageId);
|
||||||
|
GBAA(dbuf, "<input type=\"hidden\" name=\"actor\" value=\"%s\">\n", sActorId);
|
||||||
|
|
||||||
|
DBG_INFO(NULL, "Adding actor");
|
||||||
|
if (sLabel && *sLabel)
|
||||||
|
GBAA(dbuf,"<label for=\"%s\">%s</label>", sActorId, sLabel);
|
||||||
|
if (layout!=MY_LAYOUT_NONE) {
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
}
|
||||||
|
switch(AQH_Value_GetModality(value)) {
|
||||||
|
case AQH_ValueModality_RGBW: _writeRgbwToForm(sActorId, lastData, dbuf); break;
|
||||||
|
case AQH_ValueModality_OnOff: _writeOnOffToForm(sActorId, lastData, dbuf); break;
|
||||||
|
case AQH_ValueModality_OnOffAuto: _writeOnOffAutoToForm(sActorId, lastData, dbuf); break;
|
||||||
|
default: GBAA(dbuf, "%d", lastData); break;
|
||||||
|
} /* switch */
|
||||||
|
if (layout!=MY_LAYOUT_NONE) {
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
GBAS(dbuf, "<td>\n");
|
||||||
|
}
|
||||||
|
GBAS(dbuf,"<input type=\"submit\" name=\"action\" value=\"Send\"/>");
|
||||||
|
GBAS(dbuf, "</form>\n\n");
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeGraph(const char *sPageId, GWEN_XMLNODE *n, int layout, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
const char *sGraphId;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
|
||||||
|
w=GWEN_XMLNode_GetIntProperty(n, "width", AQH_MODDEVICES_GRAPH_WIDTH);
|
||||||
|
h=GWEN_XMLNode_GetIntProperty(n, "height", AQH_MODDEVICES_GRAPH_HEIGHT);
|
||||||
|
sGraphId=GWEN_XMLNode_GetProperty(n, "id", NULL);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "<td colspan=\"3\">\n");
|
||||||
|
if (sGraphId && *sGraphId)
|
||||||
|
_addGraphLink(sPageId, sGraphId, w, h, dbuf);
|
||||||
|
if (layout!=MY_LAYOUT_NONE)
|
||||||
|
GBAS(dbuf, "</td>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _addGraphLink(const char *sPageId, const char *sGraphId, int w, int h, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GBAS(dbuf, "<img src=\"pgraph.html?page=");
|
||||||
|
AQH_ModService_EscapeToBuffer(sPageId, dbuf);
|
||||||
|
GBAS(dbuf, "&graph=");
|
||||||
|
AQH_ModService_EscapeToBuffer(sGraphId, dbuf);
|
||||||
|
GBAA(dbuf, "\" alt=\"%s\" width=\"%d\" height=\"%d\"", sGraphId, w, h);
|
||||||
|
GBAS(dbuf, "/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeRgbwToForm(const char *sValueName, uint32_t color, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
DBG_ERROR(NULL, "Color=%08x (%d, %d, %d, %d)",
|
||||||
|
color,
|
||||||
|
AQH_ModDevices_RgbwGetR(color),
|
||||||
|
AQH_ModDevices_RgbwGetG(color),
|
||||||
|
AQH_ModDevices_RgbwGetB(color),
|
||||||
|
AQH_ModDevices_RgbwGetW(color));
|
||||||
|
GBAA(dbuf, "<label for=\"%s_r\">R:</label>", sValueName);
|
||||||
|
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_r\" id=name=\"%s_r\" value=\"%d\">",
|
||||||
|
sValueName, sValueName, AQH_ModDevices_RgbwGetR(color));
|
||||||
|
|
||||||
|
GBAA(dbuf, "<label for=\"%s_g\">G:</label>", sValueName);
|
||||||
|
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_g\" id=name=\"%s_g\" value=\"%d\">",
|
||||||
|
sValueName, sValueName, AQH_ModDevices_RgbwGetG(color));
|
||||||
|
|
||||||
|
GBAA(dbuf, "<label for=\"%s_b\">B:</label>", sValueName);
|
||||||
|
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_b\" id=name=\"%s_b\" value=\"%d\">",
|
||||||
|
sValueName, sValueName, AQH_ModDevices_RgbwGetB(color));
|
||||||
|
|
||||||
|
GBAA(dbuf, "<label for=\"%s_w\">W:</label>", sValueName);
|
||||||
|
GBAA(dbuf, "<input type=\"number\" min=\"0\" max=\"255\" name=\"%s_w\" id=name=\"%s_w\" value=\"%d\">",
|
||||||
|
sValueName, sValueName, AQH_ModDevices_RgbwGetW(color));
|
||||||
|
|
||||||
|
#else
|
||||||
|
GBAA(dbuf, "<input type=\"text\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>", sValueName, sValueName, color);
|
||||||
|
// else
|
||||||
|
GBAA(dbuf, "<input type=\"color\" name=\"%s\" id=\"%s\" value=\"#%08x\"/>#%08x (#%08x)",
|
||||||
|
sValueName, sValueName,
|
||||||
|
AQH_ModDevices_RgbwToHtmlColor(color),
|
||||||
|
AQH_ModDevices_RgbwToHtmlColor(color),
|
||||||
|
color);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _setRgbwData(AQH_DATACLIENT *dc, GWEN_DB_NODE *dbPost, const char *sValueName, const AQH_VALUE *value)
|
||||||
|
{
|
||||||
|
const char *sValueSystemName;
|
||||||
|
uint32_t color;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
DBG_INFO(NULL, "Set value %s", sValueName);
|
||||||
|
color=AQH_ModDevices_RgbwFromComponents(_getColorComponent(dbPost, sValueName, "r", 0),
|
||||||
|
_getColorComponent(dbPost, sValueName, "g", 0),
|
||||||
|
_getColorComponent(dbPost, sValueName, "b", 0),
|
||||||
|
_getColorComponent(dbPost, sValueName, "w", 0));
|
||||||
|
DBG_INFO(NULL, "Send value [#%08x] to %s", color, sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, (double) color);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _getColorComponent(GWEN_DB_NODE *dbPost, const char *sValueName, const char *sComponent, int defaultValue)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *buf;
|
||||||
|
const char *sData;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
buf=GWEN_Buffer_new(0, 64, 0, 1);
|
||||||
|
GBAA(buf, "%s_%s", sValueName, sComponent);
|
||||||
|
DBG_INFO(NULL, "Read value %s", GWEN_Buffer_GetStart(buf));
|
||||||
|
sData=GWEN_DB_GetCharValue(dbPost, GWEN_Buffer_GetStart(buf), 0, NULL);
|
||||||
|
GWEN_Buffer_free(buf);
|
||||||
|
if (sData) {
|
||||||
|
if (1==sscanf(sData, "%u", &result))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeOnOffToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GBAA(dbuf, "<select name=\"%s\" id=\"%s\">" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
|
||||||
|
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
|
||||||
|
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
|
||||||
|
GBAS(dbuf, "</select>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _setOnOffData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
|
||||||
|
{
|
||||||
|
if (sValue) {
|
||||||
|
const char *sValueSystemName;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
if (strcasecmp(sValue, "unchanged")==0) {
|
||||||
|
DBG_INFO(NULL, "Value %s unchanged", sValueSystemName);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sValue, "on")==0) {
|
||||||
|
DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, 1.0);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sValue, "off")==0) {
|
||||||
|
DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, 0.0);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _writeOnOffAutoToForm(const char *sValueName, int intVal, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GBAA(dbuf, "<select name=\"%s\" id=\"%s\" >" "<option value=\"unchanged\" >unchanged</option>", sValueName, sValueName);
|
||||||
|
GBAA(dbuf, "<option value=\"off\" %s>off</option>", (intVal==0)?"selected":"");
|
||||||
|
GBAA(dbuf, "<option value=\"on\" %s>on</option>", (intVal==1)?"selected":"");
|
||||||
|
GBAA(dbuf, "<option value=\"auto\" %s>auto</option>", (intVal==2)?"selected":"");
|
||||||
|
GBAS(dbuf, "</select>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _setOnOffAutoData(AQH_DATACLIENT *dc, const AQH_VALUE *value, const char *sValue)
|
||||||
|
{
|
||||||
|
if (sValue) {
|
||||||
|
const char *sValueSystemName;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sValueSystemName=AQH_Value_GetNameForSystem(value);
|
||||||
|
if (strcasecmp(sValue, "unchanged")==0) {
|
||||||
|
DBG_INFO(NULL, "Value %s unchanged", sValueSystemName);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sValue, "on")==0) {
|
||||||
|
DBG_INFO(NULL, "Send value 1 to %s", sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, 1.0);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sValue, "off")==0) {
|
||||||
|
DBG_INFO(NULL, "Send value 0 to %s", sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, 0.0);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(sValue, "auto")==0) {
|
||||||
|
DBG_INFO(NULL, "Send value 2 to %s", sValueSystemName);
|
||||||
|
rv=AQH_DataClient_SetData(dc, value, 2.0);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error sending data: %d", rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "Invalid value [%s] for %s", sValue, sValueSystemName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _sendPageList(AQH_MODULE *m, GWEN_BUFFER *dbuf)
|
||||||
|
{
|
||||||
|
GWEN_STRINGLIST *sl;
|
||||||
|
|
||||||
|
GBAS(dbuf, "<h1>Page List</h1>\n");
|
||||||
|
sl=_listPageFiles(m);
|
||||||
|
if (sl) {
|
||||||
|
GWEN_STRINGLISTENTRY *se;
|
||||||
|
|
||||||
|
GBAS(dbuf,
|
||||||
|
"<table class=\"datatable\">\n"
|
||||||
|
"<thead><tr><th>Page</th><th>Title</th></tr></thead>\n"
|
||||||
|
"<tbody>\n");
|
||||||
|
se=GWEN_StringList_FirstEntry(sl);
|
||||||
|
while(se) {
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
filename=GWEN_StringListEntry_Data(se);
|
||||||
|
if (filename && *filename) {
|
||||||
|
GWEN_XMLNODE *node;
|
||||||
|
|
||||||
|
DBG_ERROR(NULL, "Reading file \"%s\"", filename);
|
||||||
|
node=_readPageFile(filename);
|
||||||
|
if (node) {
|
||||||
|
GWEN_XMLNODE *nPage;
|
||||||
|
|
||||||
|
nPage=GWEN_XMLNode_FindFirstTag(node, "page", NULL, NULL);
|
||||||
|
if (nPage) {
|
||||||
|
const char *sId;
|
||||||
|
const char *sTitle;
|
||||||
|
|
||||||
|
sId=GWEN_XMLNode_GetProperty(nPage, "id", NULL);
|
||||||
|
sTitle=GWEN_XMLNode_GetProperty(nPage, "title", sId);
|
||||||
|
if (sId && *sId)
|
||||||
|
GBAA(dbuf, "<tr><td><a href=\"page.html?page=%s\">%s</a></td><td>%s</td></tr>\n", sId, sId, sTitle);
|
||||||
|
}
|
||||||
|
GWEN_XMLNode_free(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
se=GWEN_StringListEntry_Next(se);
|
||||||
|
}
|
||||||
|
GBAS(dbuf, "</tbody></table>");
|
||||||
|
GWEN_StringList_free(sl);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GBAS(dbuf, "No pages.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_STRINGLIST *_listPageFiles(AQH_MODULE *m)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *fbuf;
|
||||||
|
AQH_SERVICE *sv;
|
||||||
|
GWEN_STRINGLIST *sl;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
sv=AQH_ModService_GetService(m);
|
||||||
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAA(fbuf, "%s%spages", AQH_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S);
|
||||||
|
sl=GWEN_StringList_new();
|
||||||
|
rv=GWEN_Directory_GetMatchingFilesRecursively(GWEN_Buffer_GetStart(fbuf), sl, "*.xml");
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "Error reading pages (%d)", rv);
|
||||||
|
GWEN_StringList_free(sl);
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (GWEN_StringList_Count(sl)<1) {
|
||||||
|
GWEN_StringList_free(sl);
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_XMLNODE *_readPage(AQH_MODULE *m, const char *sPageName)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *fbuf;
|
||||||
|
AQH_SERVICE *sv;
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
|
||||||
|
sv=AQH_ModService_GetService(m);
|
||||||
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
GBAA(fbuf, "%s%spages%s", AQH_Service_GetRuntimeFolder(sv), GWEN_DIR_SEPARATOR_S, GWEN_DIR_SEPARATOR_S);
|
||||||
|
AQH_ModService_EscapeToBuffer(sPageName, fbuf);
|
||||||
|
GBAS(fbuf, ".xml");
|
||||||
|
|
||||||
|
fileNode=_readPageFile(GWEN_Buffer_GetStart(fbuf));
|
||||||
|
if (fileNode==NULL) {
|
||||||
|
DBG_INFO(NULL, "here");
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GWEN_Buffer_free(fbuf);
|
||||||
|
return fileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_XMLNODE *_readPageFile(const char *sFilename)
|
||||||
|
{
|
||||||
|
GWEN_XMLNODE *fileNode;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
fileNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, sFilename);
|
||||||
|
rv=GWEN_XML_ReadFile(fileNode, sFilename, GWEN_XML_FLAGS_DEFAULT);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_ERROR(NULL, "Error reading \"%s\": %s (%d)", sFilename?sFilename:"<no name>", strerror(errno), errno);
|
||||||
|
GWEN_XMLNode_free(fileNode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _layoutFromString(const char *s)
|
||||||
|
{
|
||||||
|
if (s && *s) {
|
||||||
|
if (strcasecmp(s, "none")==0)
|
||||||
|
return MY_LAYOUT_NONE;
|
||||||
|
else if (strcasecmp(s, "horizontal")==0)
|
||||||
|
return MY_LAYOUT_HORIZONTAL;
|
||||||
|
else if (strcasecmp(s, "vertical")==0)
|
||||||
|
return MY_LAYOUT_VERTICAL;
|
||||||
|
}
|
||||||
|
return MY_LAYOUT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
29
apps/aqhome-cgi/modules/devices/mdevices_page.h
Normal file
29
apps/aqhome-cgi/modules/devices/mdevices_page.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef AQHOME_CGI_MDEVICES_PAGE_H
|
||||||
|
#define AQHOME_CGI_MDEVICES_PAGE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "aqhome-cgi/modules/devices/mdevices.h"
|
||||||
|
|
||||||
|
#include "aqhome/aqhome.h"
|
||||||
|
#include "aqhome/dataclient/client.h"
|
||||||
|
|
||||||
|
#include <aqcgi/request.h>
|
||||||
|
|
||||||
|
#include <gwenhywfar/buffer.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_ModDevices_RunPageGet(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
void AQH_ModDevices_RunPagePost(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
void AQH_ModDevices_RunPageGraph(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -86,13 +86,13 @@ void AQH_ModDevices_RunSetData(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *se
|
|||||||
|
|
||||||
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
if (sValueName && *sValueName) {
|
if (sValueName && *sValueName) {
|
||||||
GBAS(pbuf, "Location: /aqbt/devices/value.html?device=");
|
GBAS(pbuf, "Location: value.html?device=");
|
||||||
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
||||||
GBAS(pbuf, "&value=");
|
GBAS(pbuf, "&value=");
|
||||||
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
|
GWEN_Text_EscapeToBuffer(sValueName, pbuf);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GBAS(pbuf, "Location: /aqbt/devices/values.html?device=");
|
GBAS(pbuf, "Location: values.html?device=");
|
||||||
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
||||||
}
|
}
|
||||||
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ void AQH_ModDevices_RunSetDevice(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *
|
|||||||
GWEN_BUFFER *pbuf;
|
GWEN_BUFFER *pbuf;
|
||||||
|
|
||||||
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
pbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
GBAS(pbuf, "Location: /aqbt/devices/device.html?device=");
|
GBAS(pbuf, "Location: device.html?device=");
|
||||||
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
GWEN_Text_EscapeToBuffer(sDeviceName, pbuf);
|
||||||
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(pbuf));
|
||||||
GWEN_Buffer_free(pbuf);
|
GWEN_Buffer_free(pbuf);
|
||||||
|
|||||||
@@ -26,10 +26,6 @@
|
|||||||
#include <gwenhywfar/timestamp.h>
|
#include <gwenhywfar/timestamp.h>
|
||||||
#include <gwenhywfar/text.h>
|
#include <gwenhywfar/text.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -42,17 +38,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum {
|
|
||||||
VALUEGRAPH_PERIOD_4H=1,
|
|
||||||
VALUEGRAPH_PERIOD_1D,
|
|
||||||
VALUEGRAPH_PERIOD_1W,
|
|
||||||
VALUEGRAPH_PERIOD_1M,
|
|
||||||
VALUEGRAPH_PERIOD_6M,
|
|
||||||
VALUEGRAPH_PERIOD_12M,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
* vars
|
* vars
|
||||||
* ------------------------------------------------------------------------------------------------
|
* ------------------------------------------------------------------------------------------------
|
||||||
@@ -97,12 +82,8 @@ static void _createGraph(AQH_DATACLIENT *dc,
|
|||||||
const MY_GRAPH_PARAMS *graphParams,
|
const MY_GRAPH_PARAMS *graphParams,
|
||||||
const char *graphTitle, int precision, const char *curveLabel,
|
const char *graphTitle, int precision, const char *curveLabel,
|
||||||
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
|
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 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 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);
|
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s);
|
||||||
|
|
||||||
|
|
||||||
@@ -167,11 +148,10 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
||||||
if (value) {
|
if (value) {
|
||||||
GWEN_BUFFER *fbuf;
|
GWEN_BUFFER *fbuf;
|
||||||
int rv;
|
|
||||||
|
|
||||||
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
|
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
|
||||||
if (!_fileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
|
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
|
||||||
DBG_DEBUG(NULL, "Creating graph");
|
DBG_DEBUG(NULL, "Creating graph");
|
||||||
_createGraph(dc,
|
_createGraph(dc,
|
||||||
value,
|
value,
|
||||||
@@ -184,23 +164,7 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
100000);
|
100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1) {
|
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
|
||||||
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);
|
GWEN_Buffer_free(fbuf);
|
||||||
AQH_Value_free(value);
|
AQH_Value_free(value);
|
||||||
@@ -212,25 +176,6 @@ void _runGraphValueWithArgs(AQH_MODULE *m,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
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,
|
void _createGraph(AQH_DATACLIENT *dc,
|
||||||
const AQH_VALUE *v,
|
const AQH_VALUE *v,
|
||||||
@@ -252,7 +197,7 @@ void _createGraph(AQH_DATACLIENT *dc,
|
|||||||
tsBegin=time(0)-(graphParams->startTimeDiff);
|
tsBegin=time(0)-(graphParams->startTimeDiff);
|
||||||
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
|
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
|
||||||
|
|
||||||
dpList=_requestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
|
dpList=AQH_ModDevices_RequestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
|
||||||
if (dpList) {
|
if (dpList) {
|
||||||
DBG_DEBUG(NULL, "Adding data for %s", sValue);
|
DBG_DEBUG(NULL, "Adding data for %s", sValue);
|
||||||
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
|
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
|
||||||
@@ -309,63 +254,13 @@ void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_
|
|||||||
|
|
||||||
/* var name */
|
/* var name */
|
||||||
s=AQH_Value_GetNameForSystem(v);
|
s=AQH_Value_GetNameForSystem(v);
|
||||||
GWEN_Text_EscapeToBuffer(s, dbuf);
|
AQH_ModService_EscapeToBuffer(s, dbuf);
|
||||||
|
|
||||||
GBAA(dbuf, "-%s.png", graphParams->name);
|
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 *_getParamsByName(const char *s)
|
||||||
{
|
{
|
||||||
const MY_GRAPH_PARAMS *p;
|
const MY_GRAPH_PARAMS *p;
|
||||||
|
|||||||
12
apps/aqhome-cgi/modules/html/0BUILD
Normal file
12
apps/aqhome-cgi/modules/html/0BUILD
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml?>
|
||||||
|
|
||||||
|
<gwbuild>
|
||||||
|
|
||||||
|
<data dist="true" install="$(httpdatadir)/aqhome-cgi/html">
|
||||||
|
style.css
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<subdirs>
|
||||||
|
pics
|
||||||
|
</subdirs>
|
||||||
|
</gwbuild>
|
||||||
17
apps/aqhome-cgi/modules/html/pics/0BUILD
Normal file
17
apps/aqhome-cgi/modules/html/pics/0BUILD
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml?>
|
||||||
|
|
||||||
|
<gwbuild>
|
||||||
|
|
||||||
|
<data dist="true" install="$(httpdatadir)/aqhome-cgi/html/pics">
|
||||||
|
cancel.png
|
||||||
|
document-table.png
|
||||||
|
edit.png
|
||||||
|
graph.png
|
||||||
|
minus.png
|
||||||
|
ok.png
|
||||||
|
plus.png
|
||||||
|
user-add.png
|
||||||
|
user-edit.png
|
||||||
|
</data>
|
||||||
|
|
||||||
|
</gwbuild>
|
||||||
BIN
apps/aqhome-cgi/modules/html/pics/document-table.png
Normal file
BIN
apps/aqhome-cgi/modules/html/pics/document-table.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
apps/aqhome-cgi/modules/html/pics/user-add.png
Normal file
BIN
apps/aqhome-cgi/modules/html/pics/user-add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
apps/aqhome-cgi/modules/html/pics/user-edit.png
Normal file
BIN
apps/aqhome-cgi/modules/html/pics/user-edit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -97,7 +97,7 @@ AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *sessio
|
|||||||
|
|
||||||
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
nbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
s=AQH_ModService_GetBaseFolder(m);
|
s=AQH_ModService_GetBaseFolder(m);
|
||||||
GWEN_Buffer_AppendArgs(nbuf, "%s/devices", s?s:".");
|
GWEN_Buffer_AppendArgs(nbuf, "%s/admin", s?s:".");
|
||||||
|
|
||||||
AQH_ModAdmin_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
|
AQH_ModAdmin_Extend(mSub, AQH_ModService_GetService(m), GWEN_Buffer_GetStart(nbuf));
|
||||||
AQH_Module_Tree2_AddChild(m, mSub);
|
AQH_Module_Tree2_AddChild(m, mSub);
|
||||||
|
|||||||
@@ -15,11 +15,12 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<ul class="mainmenu" >
|
<ul class="mainmenu" >
|
||||||
<li><a href="/aqbt/devices/index.html">Devices</a></li>
|
<li><a href="/aqhome/devices/index.html">Devices</a></li>
|
||||||
<li><a href="/aqbt/admin/index.html">Admin</a></li>
|
<li><a href="/aqhome/devices/page.html">Pages</a></li>
|
||||||
|
<li><a href="/aqhome/admin/index.html">Admin</a></li>
|
||||||
<li><a href="#news">News</a></li>
|
<li><a href="#news">News</a></li>
|
||||||
<li><a href="#contact">Contact</a></li>
|
<li><a href="#contact">Contact</a></li>
|
||||||
<li style="float:right"><a href="/aqbt/login">Login</a></li>
|
<li style="float:right"><a href="/aqhome/login">Login</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -79,7 +79,8 @@ static int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint);
|
|||||||
static int _handleClientDown(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 _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 _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 _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver,
|
||||||
|
const char *deviceName, const char *valueNameForSystem, const AQH_VALUE *valueTemplate);
|
||||||
static void _updateValue(AQHOME_SERVER *xo, AQH_VALUE *v, const char *deviceName, const AQH_VALUE *valueTemplate);
|
static 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 AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName);
|
||||||
static int _createPidFile(const char *pidFilename);
|
static int _createPidFile(const char *pidFilename);
|
||||||
@@ -655,7 +656,7 @@ AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o,
|
|||||||
v=AQH_Storage_GetValueByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
v=AQH_Storage_GetValueByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
||||||
if (v==NULL) {
|
if (v==NULL) {
|
||||||
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE)
|
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE)
|
||||||
_createValue(xo, epDriver, GWEN_Buffer_GetStart(buf), valueTemplate);
|
_createValue(xo, epDriver, deviceName, GWEN_Buffer_GetStart(buf), valueTemplate);
|
||||||
else {
|
else {
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
|
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||||
GWEN_Buffer_free(buf);
|
GWEN_Buffer_free(buf);
|
||||||
@@ -672,20 +673,25 @@ AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName, const AQH_VALUE *valueTemplate)
|
void _createValue(AQHOME_SERVER *xo, AQH_OBJECT *epDriver,
|
||||||
|
const char *deviceName, const char *valueNameForSystem, const AQH_VALUE *valueTemplate)
|
||||||
{
|
{
|
||||||
AQH_DEVICE *device;
|
AQH_DEVICE *device;
|
||||||
const char *serviceName;
|
const char *serviceName;
|
||||||
|
const char *valueName;
|
||||||
AQH_VALUE *v;
|
AQH_VALUE *v;
|
||||||
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", deviceName);
|
|
||||||
serviceName=AQH_Endpoint_GetServiceName(epDriver);
|
serviceName=AQH_Endpoint_GetServiceName(epDriver);
|
||||||
|
valueName=AQH_Value_GetName(valueTemplate);
|
||||||
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
|
device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL;
|
||||||
|
|
||||||
|
DBG_ERROR(AQH_LOGDOMAIN,
|
||||||
|
"Creating value: service=\"%s\", device=\"%s\", value \"%s\"",
|
||||||
|
serviceName?serviceName:"<no service>", deviceName?deviceName:"<no device>", valueName?valueName:"<no value>");
|
||||||
v=AQH_Value_new();
|
v=AQH_Value_new();
|
||||||
AQH_Value_SetDriver(v, serviceName);
|
AQH_Value_SetDriver(v, serviceName);
|
||||||
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
|
AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate));
|
||||||
AQH_Value_SetNameForSystem(v, deviceName);
|
AQH_Value_SetNameForSystem(v, valueNameForSystem);
|
||||||
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
|
AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate));
|
||||||
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
|
AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate));
|
||||||
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
|
AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate));
|
||||||
@@ -755,7 +761,7 @@ AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver,
|
|||||||
device=AQH_Storage_GetDeviceByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
device=AQH_Storage_GetDeviceByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf));
|
||||||
if (device==NULL) {
|
if (device==NULL) {
|
||||||
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDDEVICE) {
|
if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDDEVICE) {
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "Creating device \"%s\"", GWEN_Buffer_GetStart(buf));
|
DBG_ERROR(AQH_LOGDOMAIN, "Creating device \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||||
device=AQH_Device_new();
|
device=AQH_Device_new();
|
||||||
AQH_Device_SetDriver(device, serviceName);
|
AQH_Device_SetDriver(device, serviceName);
|
||||||
AQH_Device_SetName(device, deviceName);
|
AQH_Device_SetName(device, deviceName);
|
||||||
|
|||||||
1
apps/aqhome-storage/.gitignore
vendored
1
apps/aqhome-storage/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
test/
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<?xml?>
|
|
||||||
|
|
||||||
<gwbuild>
|
|
||||||
|
|
||||||
<target type="Program" name="aqhome-storage" install="$(bindir)" >
|
|
||||||
|
|
||||||
<includes type="c" >
|
|
||||||
$(gwenhywfar_cflags)
|
|
||||||
-I$(topsrcdir)
|
|
||||||
-I$(topbuilddir)
|
|
||||||
-I$(builddir)
|
|
||||||
-I$(srcdir)
|
|
||||||
</includes>
|
|
||||||
|
|
||||||
<includes type="tm2" >
|
|
||||||
--include=$(builddir)
|
|
||||||
--include=$(srcdir)
|
|
||||||
</includes>
|
|
||||||
|
|
||||||
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
|
||||||
|
|
||||||
<setVar name="tm2flags" >
|
|
||||||
</setVar>
|
|
||||||
|
|
||||||
<setVar name="local/typefiles" >
|
|
||||||
</setVar>
|
|
||||||
|
|
||||||
<setVar name="local/built_sources" >
|
|
||||||
</setVar>
|
|
||||||
|
|
||||||
<setVar name="local/built_headers_pub">
|
|
||||||
</setVar>
|
|
||||||
|
|
||||||
<setVar name="local/built_headers_priv" >
|
|
||||||
</setVar>
|
|
||||||
|
|
||||||
<headers dist="true" >
|
|
||||||
aqhomestorage_p.h
|
|
||||||
aqhomestorage.h
|
|
||||||
init.h
|
|
||||||
init_http.h
|
|
||||||
init_mqtt.h
|
|
||||||
fini.h
|
|
||||||
loop.h
|
|
||||||
loop_http.h
|
|
||||||
loop_mqtt.h
|
|
||||||
cleanup.h
|
|
||||||
u_base.h
|
|
||||||
u_login.h
|
|
||||||
u_objects.h
|
|
||||||
u_objects_p.h
|
|
||||||
u_rooms.h
|
|
||||||
u_devices.h
|
|
||||||
u_mqtttopics.h
|
|
||||||
u_values.h
|
|
||||||
u_static.h
|
|
||||||
u_static_p.h
|
|
||||||
aqhomehttp.h
|
|
||||||
aqhomehttp_p.h
|
|
||||||
</headers>
|
|
||||||
|
|
||||||
<sources>
|
|
||||||
$(local/typefiles)
|
|
||||||
|
|
||||||
aqhomestorage.c
|
|
||||||
init.c
|
|
||||||
init_http.c
|
|
||||||
init_mqtt.c
|
|
||||||
fini.c
|
|
||||||
loop.c
|
|
||||||
loop_http.c
|
|
||||||
loop_mqtt.c
|
|
||||||
cleanup.c
|
|
||||||
u_base.c
|
|
||||||
u_login.c
|
|
||||||
u_objects.c
|
|
||||||
u_rooms.c
|
|
||||||
u_devices.c
|
|
||||||
u_mqtttopics.c
|
|
||||||
u_values.c
|
|
||||||
u_static.c
|
|
||||||
main.c
|
|
||||||
aqhomehttp.c
|
|
||||||
</sources>
|
|
||||||
|
|
||||||
<useTargets>
|
|
||||||
aqhome
|
|
||||||
</useTargets>
|
|
||||||
|
|
||||||
<libraries>
|
|
||||||
$(gwenhywfar_libs)
|
|
||||||
</libraries>
|
|
||||||
|
|
||||||
<subdirs>
|
|
||||||
</subdirs>
|
|
||||||
|
|
||||||
|
|
||||||
<extradist>
|
|
||||||
</extradist>
|
|
||||||
|
|
||||||
|
|
||||||
</target>
|
|
||||||
|
|
||||||
|
|
||||||
</gwbuild>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
- isolate storage service:
|
|
||||||
- remove http service from here
|
|
||||||
- add ipc service
|
|
||||||
- admin:
|
|
||||||
- add room/device/MQTT topic/value
|
|
||||||
- edit room/device/MQTT topic/value
|
|
||||||
- del room/device/MQTT topic/value
|
|
||||||
- get received topics
|
|
||||||
- list rooms/devices/MQTT topics/values
|
|
||||||
- getValues(valueId, timeFrom, timeUntil)
|
|
||||||
- addValue(valueId/valueName, timeStamp, value)
|
|
||||||
- aqhome-tool
|
|
||||||
- add ipc admin code to connect to ipc service
|
|
||||||
- create http service as stand-alone app or create PHP code which uses aqhome-tool
|
|
||||||
- connect to storage service for information/admin
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- move http service into own folder
|
|
||||||
- isolate functions:
|
|
||||||
- getRoomList
|
|
||||||
- getDeviceList
|
|
||||||
- getTopicList
|
|
||||||
- addRoom/Device/Topic/Value
|
|
||||||
- delRoom/Device/Topic/Value
|
|
||||||
- editRoom/Device/Topic/Value
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- aqhome-storage->aqhome-data
|
|
||||||
|
|
||||||
- aqhome-data
|
|
||||||
- only list of values
|
|
||||||
- manages datafiles only
|
|
||||||
- IPC (later secure ipc):
|
|
||||||
- get value list
|
|
||||||
- add value
|
|
||||||
- del value
|
|
||||||
- edit value
|
|
||||||
|
|
||||||
- add datapoint(valueId, timestamp, data)
|
|
||||||
- add datapoint(valueName, timestamp, data)
|
|
||||||
|
|
||||||
- getData(valueId, timeFrom, timeUntil)
|
|
||||||
- getLastData(valueId)
|
|
||||||
|
|
||||||
- aqhome-tool:
|
|
||||||
- add ipc code
|
|
||||||
|
|
||||||
- aqhome-mqttdata:
|
|
||||||
- use devices, topics and values from aqhome-storage
|
|
||||||
- derive valueid and data from mqtt messages
|
|
||||||
- send data to aqhome-data via ipc (later secure ipc)
|
|
||||||
|
|
||||||
|
|
||||||
- mqtt values:
|
|
||||||
- aliases (e.g. for doors: open=1.0, closed=0.0, tilted=0.5)
|
|
||||||
|
|
||||||
|
|
||||||
- later services only:
|
|
||||||
- aqhomed (tty, ipcd)
|
|
||||||
- aqhome-data (ipcd)
|
|
||||||
- aqhome-mqtt (mqttc, ipcd)
|
|
||||||
- aqhome-httpd (maybe; httpd, ipcc)
|
|
||||||
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomehttp_p.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/db.h>
|
|
||||||
#include <gwenhywfar/buffer.h>
|
|
||||||
#include <gwenhywfar/text.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_INHERIT(AQH_SERVICE, AQHOME_HTTP)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
void AqHomeHttpService_Extend(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
GWEN_NEW_OBJECT(AQHOME_HTTP, xsv);
|
|
||||||
GWEN_INHERIT_SETDATA(AQH_SERVICE, AQHOME_HTTP, sv, xsv, _freeData);
|
|
||||||
|
|
||||||
xsv->contentTree=AQH_HttpContent_new("root");
|
|
||||||
xsv->storageMutex=GWEN_Mutex_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _freeData(void *bp, void *p)
|
|
||||||
{
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=(AQHOME_HTTP*) p;
|
|
||||||
|
|
||||||
xsv->storage=NULL;
|
|
||||||
AQH_HttpContent_free(xsv->contentTree);
|
|
||||||
xsv->contentTree=NULL;
|
|
||||||
GWEN_Mutex_free(xsv->storageMutex);
|
|
||||||
|
|
||||||
GWEN_FREE_OBJECT(xsv);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv)
|
|
||||||
return xsv->storage;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeHttpService_SetStorage(AQH_SERVICE *sv, AQH_STORAGE *sto)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv)
|
|
||||||
xsv->storage=sto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv)
|
|
||||||
return xsv->contentTree;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv) {
|
|
||||||
AQH_HttpContent_free(xsv->contentTree);
|
|
||||||
xsv->contentTree=c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeHttpService_MarkStorageChanged(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv) {
|
|
||||||
AQH_Storage_AddRuntimeFlags(xsv->storage, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeHttpService_LockStorage(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=GWEN_Mutex_Lock(xsv->storageMutex);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeHttpService_UnlockStorage(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
if (sv) {
|
|
||||||
AQHOME_HTTP *xsv;
|
|
||||||
|
|
||||||
xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv);
|
|
||||||
if (xsv) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=GWEN_Mutex_Unlock(xsv->storageMutex);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_HTTP_H
|
|
||||||
#define AQHOME_HTTP_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/service/service.h"
|
|
||||||
#include "aqhome/data/storage.h"
|
|
||||||
#include "aqhome/http/content.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define AQHOME_HTTP_PERMS_LIST_ROOMS 0x00000001
|
|
||||||
#define AQHOME_HTTP_PERMS_LIST_DEVICES 0x00000002
|
|
||||||
#define AQHOME_HTTP_PERMS_LIST_VALUES 0x00000004
|
|
||||||
#define AQHOME_HTTP_PERMS_LIST_TOPICS 0x00000008
|
|
||||||
|
|
||||||
#define AQHOME_HTTP_PERMS_ADD_ROOM 0x00000100
|
|
||||||
#define AQHOME_HTTP_PERMS_ADD_DEVICE 0x00000200
|
|
||||||
#define AQHOME_HTTP_PERMS_ADD_VALUE 0x00000400
|
|
||||||
#define AQHOME_HTTP_PERMS_ADD_TOPIC 0x00000800
|
|
||||||
|
|
||||||
#define AQHOME_HTTP_PERMS_DEL_ROOM 0x00010000
|
|
||||||
#define AQHOME_HTTP_PERMS_DEL_DEVICE 0x00020000
|
|
||||||
#define AQHOME_HTTP_PERMS_DEL_VALUE 0x00040000
|
|
||||||
#define AQHOME_HTTP_PERMS_DEL_TOPIC 0x00080000
|
|
||||||
|
|
||||||
#define AQHOME_HTTP_PERMS_EDIT_ROOM 0x01000000
|
|
||||||
#define AQHOME_HTTP_PERMS_EDIT_DEVICE 0x02000000
|
|
||||||
#define AQHOME_HTTP_PERMS_EDIT_VALUE 0x04000000
|
|
||||||
#define AQHOME_HTTP_PERMS_EDIT_TOPIC 0x08000000
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeHttpService_Extend(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
AQH_STORAGE *AqHomeHttpService_GetStorage(const AQH_SERVICE *sv);
|
|
||||||
void AqHomeHttpService_SetStorage(AQH_SERVICE *sv, AQH_STORAGE *sto);
|
|
||||||
|
|
||||||
AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv);
|
|
||||||
void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c);
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeHttpService_LockStorage(AQH_SERVICE *sv);
|
|
||||||
int AqHomeHttpService_UnlockStorage(AQH_SERVICE *sv);
|
|
||||||
void AqHomeHttpService_MarkStorageChanged(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_HTTP_P_H
|
|
||||||
#define AQHOME_HTTP_P_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/mutex.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct AQHOME_HTTP AQHOME_HTTP;
|
|
||||||
struct AQHOME_HTTP {
|
|
||||||
AQH_STORAGE *storage; /* do not release */
|
|
||||||
GWEN_MUTEX *storageMutex;
|
|
||||||
|
|
||||||
AQH_HTTP_CONTENT *contentTree;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/misc.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQHOME_STORAGE *AqHomeStorage_new()
|
|
||||||
{
|
|
||||||
AQHOME_STORAGE *aqh;
|
|
||||||
|
|
||||||
GWEN_NEW_OBJECT(AQHOME_STORAGE, aqh);
|
|
||||||
aqh->rootEndpoint=GWEN_MsgEndpoint_new("root", 0);
|
|
||||||
|
|
||||||
return aqh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_free(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
if (aqh) {
|
|
||||||
AQH_Service_free(aqh->httpService);
|
|
||||||
aqh->httpService=NULL;
|
|
||||||
AQH_Storage_free(aqh->storage);
|
|
||||||
aqh->storage=NULL;
|
|
||||||
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
|
|
||||||
aqh->rootEndpoint=NULL;
|
|
||||||
aqh->ipcdEndpoint=NULL;
|
|
||||||
aqh->mqttEndpoint=NULL;
|
|
||||||
aqh->httpdEndpoint=NULL;
|
|
||||||
GWEN_DB_Group_free(aqh->dbArgs);
|
|
||||||
aqh->dbArgs=NULL;
|
|
||||||
free(aqh->pidFile);
|
|
||||||
|
|
||||||
GWEN_FREE_OBJECT(aqh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetRootEndpoint(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->rootEndpoint):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetIpcdEndpoint(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->ipcdEndpoint):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetMqttEndpoint(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->mqttEndpoint):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetHttpdEndpoint(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->httpdEndpoint):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *AqHomeStorage_GetDbArgs(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->dbArgs):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_STORAGE *AqHomeStorage_GetStorage(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?(aqh->storage):NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const char *AqHomeStorage_GetPidFile(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?aqh->pidFile:NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s)
|
|
||||||
{
|
|
||||||
if (aqh) {
|
|
||||||
free(aqh->pidFile);
|
|
||||||
aqh->pidFile=s?strdup(s):NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_GetTimeout(const AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
return aqh?aqh->timeout:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_H
|
|
||||||
#define AQHOME_STORAGE_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/data/storage.h"
|
|
||||||
#include "aqhome/service/session.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct AQHOME_STORAGE AQHOME_STORAGE;
|
|
||||||
|
|
||||||
|
|
||||||
AQHOME_STORAGE *AqHomeStorage_new();
|
|
||||||
void AqHomeStorage_free(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetRootEndpoint(const AQHOME_STORAGE *aqh);
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetIpcdEndpoint(const AQHOME_STORAGE *aqh);
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetMqttEndpoint(const AQHOME_STORAGE *aqh);
|
|
||||||
GWEN_MSG_ENDPOINT *AqHomeStorage_GetHttpdEndpoint(const AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
GWEN_DB_NODE *AqHomeStorage_GetDbArgs(const AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
AQH_STORAGE *AqHomeStorage_GetStorage(const AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
const char *AqHomeStorage_GetPidFile(const AQHOME_STORAGE *aqh);
|
|
||||||
void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s);
|
|
||||||
|
|
||||||
int AqHomeStorage_GetTimeout(const AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_P_H
|
|
||||||
#define AQHOME_STORAGE_P_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* default values */
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_PIDFILE "/var/run/aqhome-storage.pid"
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_IPC_PORT 45455
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_HTTP_PORT 45456
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_MQTT_CLIENTID "AQHOMESTORAGE"
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE 600
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_MQTT_PORT 1883
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_CONFIGDIR "/var/lib/aqhomestorage/config"
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_HTTP_SOURCEDIR "/var/lib/aqhomestorage/html"
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_DATADIR "/var/lib/aqhomestorage/data"
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_STATEFILE "/var/lib/aqhomestorage/config/statefile"
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_MAXSESSIONAGE (30*60)
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_SITEHEADER "site-header.html"
|
|
||||||
#define AQHOME_STORAGE_SITEFOOTER "site-footer.html"
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_STATIC_RELFOLDER "static"
|
|
||||||
|
|
||||||
|
|
||||||
struct AQHOME_STORAGE {
|
|
||||||
GWEN_MSG_ENDPOINT *rootEndpoint;
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *ipcdEndpoint; /* don't release, will be released by freeing rootEndpoint! */
|
|
||||||
GWEN_MSG_ENDPOINT *mqttEndpoint; /* don't release, will be released by freeing rootEndpoint! */
|
|
||||||
GWEN_MSG_ENDPOINT *httpdEndpoint; /* don't release, will be released by freeing rootEndpoint! */
|
|
||||||
|
|
||||||
GWEN_DB_NODE *dbArgs;
|
|
||||||
|
|
||||||
AQH_SERVICE *httpService;
|
|
||||||
AQH_STORAGE *storage;
|
|
||||||
|
|
||||||
char *pidFile;
|
|
||||||
|
|
||||||
int maxSessionAgeInSeconds;
|
|
||||||
|
|
||||||
int timeout; /* timeout for run e.g. inside valgrind */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./cleanup.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_Cleanup(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=AQH_HttpService_CleanupSessions(aqh->httpService, aqh->maxSessionAgeInSeconds);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_CLEANUP_H
|
|
||||||
#define AQHOME_STORAGE_CLEANUP_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_Cleanup(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./fini.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint_tcpd.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void _disconnectTree(GWEN_MSG_ENDPOINT *ep);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
void AqHomeStorage_Fini(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
if (aqh) {
|
|
||||||
if (aqh->rootEndpoint) {
|
|
||||||
_disconnectTree(aqh->rootEndpoint);
|
|
||||||
GWEN_MsgEndpoint_Disconnect(aqh->rootEndpoint);
|
|
||||||
}
|
|
||||||
GWEN_MsgEndpoint_free(aqh->rootEndpoint);
|
|
||||||
aqh->rootEndpoint=NULL;
|
|
||||||
aqh->ipcdEndpoint=NULL;
|
|
||||||
aqh->httpdEndpoint=NULL;
|
|
||||||
aqh->mqttEndpoint=NULL;
|
|
||||||
|
|
||||||
if (aqh->pidFile)
|
|
||||||
remove(aqh->pidFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _disconnectTree(GWEN_MSG_ENDPOINT *ep)
|
|
||||||
{
|
|
||||||
GWEN_MSG_ENDPOINT *epChild;
|
|
||||||
|
|
||||||
epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep);
|
|
||||||
while(epChild) {
|
|
||||||
_disconnectTree(epChild);
|
|
||||||
epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild);
|
|
||||||
} /* while */
|
|
||||||
|
|
||||||
GWEN_MsgEndpoint_Disconnect(ep);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_FINI_H
|
|
||||||
#define AQHOME_STORAGE_FINI_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_Fini(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- copyright (c) 2023 by martin -->
|
|
||||||
<meta name="generator" content="FTE 1.1" />
|
|
||||||
<meta name="revised" content="martin,2023-07-23" />
|
|
||||||
<meta name="keywords" content="" />
|
|
||||||
<meta name="description" content="" />
|
|
||||||
<meta name="author" content="martin" />
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
|
|
||||||
<title>AqHome Storage Service</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
@@ -1,506 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./init.h"
|
|
||||||
#include "./init_http.h"
|
|
||||||
#include "./init_mqtt.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include "aqhome/msg/endpoint_tty.h"
|
|
||||||
#include "aqhome/ipc/endpoint_ipc.h"
|
|
||||||
#include "aqhome/mqtt/endpoint_mqttc.h"
|
|
||||||
#include "aqhome/http/endpoint_http.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint_tcpd.h>
|
|
||||||
#include <gwenhywfar/endpoint_msgio.h>
|
|
||||||
#include <gwenhywfar/directory.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
|
||||||
# include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
# include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define I18N(msg) msg
|
|
||||||
#define I18S(msg) msg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _setupFolders(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
|
|
||||||
static int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
|
|
||||||
static void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
|
|
||||||
static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data);
|
|
||||||
|
|
||||||
static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
|
|
||||||
static int _createPidFile(const char *pidFilename);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv)
|
|
||||||
{
|
|
||||||
GWEN_DB_NODE *dbArgs;
|
|
||||||
int rv;
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
dbArgs=GWEN_DB_Group_new("args");
|
|
||||||
rv=_readArgs(argc, argv, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error reading args (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
aqh->dbArgs=dbArgs;
|
|
||||||
|
|
||||||
aqh->maxSessionAgeInSeconds=GWEN_DB_GetIntValue(dbArgs, "maxSessionAge", 0, AQHOME_STORAGE_DEFAULT_MAXSESSIONAGE);
|
|
||||||
aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
|
|
||||||
|
|
||||||
s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_STORAGE_DEFAULT_PIDFILE);
|
|
||||||
if (s && *s) {
|
|
||||||
AqHomeStorage_SetPidFile(aqh, s);
|
|
||||||
rv=_createPidFile(s);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_setupFolders(aqh, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_setupStorage(aqh, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setupIpc(aqh, dbArgs);
|
|
||||||
|
|
||||||
rv=AqHomeStorage_SetupMqtt(aqh, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeStorage_SetupHttp(aqh, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _setupFolders(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *s;
|
|
||||||
GWEN_BUFFER *nameBuf;
|
|
||||||
int pos;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
s=GWEN_DB_GetCharValue(dbArgs, "cfgdir", 0, AQHOME_STORAGE_DEFAULT_CONFIGDIR);
|
|
||||||
if (!(s && *s)) {
|
|
||||||
DBG_ERROR(NULL, "Missing configuration folder");
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, s);
|
|
||||||
pos=GWEN_Buffer_GetPos(nameBuf);
|
|
||||||
rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error accessing configuration folder \"%s\"", GWEN_Buffer_GetStart(nameBuf));
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
GWEN_Buffer_Crop(nameBuf, 0, pos);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S "modules");
|
|
||||||
rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error accessing modules folder \"%s\"", GWEN_Buffer_GetStart(nameBuf));
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
GWEN_Buffer_Crop(nameBuf, 0, pos);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S "users");
|
|
||||||
rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf), GWEN_PATH_FLAGS_CHECKROOT);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error accessing configuration folder \"%s\"", GWEN_Buffer_GetStart(nameBuf));
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _setupStorage(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *dataFolder;
|
|
||||||
const char *stateFile;
|
|
||||||
|
|
||||||
dataFolder=GWEN_DB_GetCharValue(dbArgs, "dataFolder", 0, AQHOME_STORAGE_DEFAULT_DATADIR);
|
|
||||||
stateFile=GWEN_DB_GetCharValue(dbArgs, "stateFile", 0, NULL);
|
|
||||||
if (stateFile && *stateFile) {
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sto=AQH_Storage_new();
|
|
||||||
AQH_Storage_SetStateFile(sto, stateFile);
|
|
||||||
|
|
||||||
AQH_Storage_SetDataFileFolder(sto, (dataFolder && *dataFolder)?dataFolder:NULL);
|
|
||||||
|
|
||||||
rv=AQH_Storage_Init(sto);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
AQH_Storage_free(sto);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
aqh->storage=sto;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "No state file given");
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setupIpc(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *tcpAddress;
|
|
||||||
int tcpPort;
|
|
||||||
|
|
||||||
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL);
|
|
||||||
tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, AQHOME_STORAGE_DEFAULT_IPC_PORT);
|
|
||||||
|
|
||||||
if (tcpAddress && *tcpAddress && tcpPort) {
|
|
||||||
GWEN_MSG_ENDPOINT *ep;
|
|
||||||
|
|
||||||
ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0);
|
|
||||||
GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh);
|
|
||||||
|
|
||||||
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
|
|
||||||
aqh->ipcdEndpoint=ep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep,
|
|
||||||
GWEN_SOCKET *sk,
|
|
||||||
const GWEN_INETADDRESS *addr,
|
|
||||||
GWEN_UNUSED void *data)
|
|
||||||
{
|
|
||||||
/* AQHOME_STORAGE *aqh;
|
|
||||||
*
|
|
||||||
* aqh=(AQHOME_STORAGE*) data;
|
|
||||||
*/
|
|
||||||
DBG_INFO(NULL, "Incoming IPC connection");
|
|
||||||
return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createPidFile(const char *pidFilename)
|
|
||||||
{
|
|
||||||
FILE *f;
|
|
||||||
int pidfd;
|
|
||||||
|
|
||||||
if (remove(pidFilename)==0) {
|
|
||||||
DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
||||||
if (pidfd < 0) {
|
|
||||||
DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
f = fdopen(pidfd, "w");
|
|
||||||
#else /* HAVE_STAT_H */
|
|
||||||
f=fopen(pidFilename,"w+");
|
|
||||||
#endif /* HAVE_STAT_H */
|
|
||||||
|
|
||||||
/* write pid */
|
|
||||||
#ifdef HAVE_GETPID
|
|
||||||
fprintf(f,"%d\n",getpid());
|
|
||||||
#else
|
|
||||||
fprintf(f,"-1\n");
|
|
||||||
#endif
|
|
||||||
if (fclose(f)) {
|
|
||||||
DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno));
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
const GWEN_ARGS args[]= {
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"cfgdir", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"D", /* short option */
|
|
||||||
"cfgdir", /* long option */
|
|
||||||
I18S("Specify the configuration folder"),
|
|
||||||
I18S("Specify the configuration folder")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"tcpAddress", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"t", /* short option */
|
|
||||||
"tcpaddress", /* long option */
|
|
||||||
I18S("Specify the TCP address to listen on (disabled if missing)"),
|
|
||||||
I18S("Specify the TCP address to listen on (disabled if missing)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"tcpPort", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"P", /* short option */
|
|
||||||
"tcpport", /* long option */
|
|
||||||
I18S("Specify the TCP port to listen on"),
|
|
||||||
I18S("Specify the TCP port to listen on")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"mqttAddress", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"ma", /* short option */
|
|
||||||
"mqttaddress", /* long option */
|
|
||||||
I18S("Specify the address of the MQTT server to connect to (disabled if missing)"),
|
|
||||||
I18S("Specify the address of the MQTT server to connect to (disabled if missing)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"mqttPort", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"mp", /* short option */
|
|
||||||
"mqttport", /* long option */
|
|
||||||
I18S("Specify the port of the MQTT server (default: 1883)"),
|
|
||||||
I18S("Specify the port of the MQTT server (default: 1883)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"mqttClientId", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
NULL, /* short option */
|
|
||||||
"mqttclientid", /* long option */
|
|
||||||
I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")"),
|
|
||||||
I18S("Specify client id for the MQTT server (default: \"AQHOMESTORAGE\")")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"mqttKeepAlive", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"mk", /* short option */
|
|
||||||
"mqttkeepalive", /* long option */
|
|
||||||
I18S("Specify keepalive time in seconds (defaults: 600)"),
|
|
||||||
I18S("Specify keepalive time in seconds (defaults: 600)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"httpAddress", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"ha", /* short option */
|
|
||||||
"httpaddress", /* long option */
|
|
||||||
I18S("Specify the address to bind the http service to (disabled if missing)"),
|
|
||||||
I18S("Specify the address to bind the http service to (disabled if missing)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"httpPort", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"hp", /* short option */
|
|
||||||
"httpport", /* long option */
|
|
||||||
I18S("Specify the port to listen on for HTTP connections"),
|
|
||||||
I18S("Specify the port to listen on for HTTP connections")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"maxSessionAge", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
NULL, /* short option */
|
|
||||||
"maxsessionage", /* long option */
|
|
||||||
I18S("Specify maximum session age in seconds (default: 30mins)"),
|
|
||||||
I18S("Specify maximum session age in seconds (default: 30mins)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"sourcefolder", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
NULL, /* short option */
|
|
||||||
"sourcefolder", /* long option */
|
|
||||||
I18S("Folder where static HTML source files are stored"),
|
|
||||||
I18S("Folder where static HTML source files are stored")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"datafolder", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
NULL, /* short option */
|
|
||||||
"datafolder", /* long option */
|
|
||||||
I18S("Folder where data files are stored"),
|
|
||||||
I18S("Folder where data files are stored")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"statefile", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"S", /* short option */
|
|
||||||
"statefile", /* long option */
|
|
||||||
I18S("File where rooms, devices and values etc. are stored"),
|
|
||||||
I18S("File where rooms, devices and values etc. are stored")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Char, /* type */
|
|
||||||
"pidfile", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"p", /* short option */
|
|
||||||
"pidfile", /* long option */
|
|
||||||
I18S("Specify the PID file"),
|
|
||||||
I18S("Specify the PID file")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"timeout", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
1, /* maxnum */
|
|
||||||
"T", /* short option */
|
|
||||||
"timeout", /* long option */
|
|
||||||
I18S("Specify timeout in second (default: no timeout)"),
|
|
||||||
I18S("Specify timeout in second (default: no timeout)")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */
|
|
||||||
GWEN_ArgsType_Int, /* type */
|
|
||||||
"help", /* name */
|
|
||||||
0, /* minnum */
|
|
||||||
0, /* maxnum */
|
|
||||||
"h", /* short option */
|
|
||||||
"help",
|
|
||||||
I18S("Show this help screen."),
|
|
||||||
I18S("Show this help screen.")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs);
|
|
||||||
if (rv==GWEN_ARGS_RESULT_ERROR) {
|
|
||||||
fprintf(stderr, "ERROR: Could not parse arguments main\n");
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
else if (rv==GWEN_ARGS_RESULT_HELP) {
|
|
||||||
GWEN_BUFFER *ubuf;
|
|
||||||
|
|
||||||
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
|
||||||
GWEN_Buffer_AppendArgs(ubuf,
|
|
||||||
I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"),
|
|
||||||
AQHOME_VERSION_STRING,
|
|
||||||
argv[0]);
|
|
||||||
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
|
|
||||||
fprintf(stderr, "ERROR: Could not create help string\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(ubuf, "\n");
|
|
||||||
|
|
||||||
fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf));
|
|
||||||
GWEN_Buffer_free(ubuf);
|
|
||||||
return GWEN_ERROR_CLOSE;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_INIT_H
|
|
||||||
#define AQHOME_STORAGE_INIT_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./init_http.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "./u_login.h"
|
|
||||||
#include "./u_rooms.h"
|
|
||||||
#include "./u_devices.h"
|
|
||||||
#include "./u_mqtttopics.h"
|
|
||||||
#include "./u_values.h"
|
|
||||||
#include "./u_static.h"
|
|
||||||
|
|
||||||
#include "aqhome/msg/endpoint_tty.h"
|
|
||||||
#include "aqhome/ipc/endpoint_ipc.h"
|
|
||||||
#include "aqhome/mqtt/endpoint_mqttc.h"
|
|
||||||
#include "aqhome/http/endpoint_http.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
#include "aqhome/http/content_files.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint_tcpd.h>
|
|
||||||
#include <gwenhywfar/endpoint_msgio.h>
|
|
||||||
#include <gwenhywfar/directory.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
|
||||||
# include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
# include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define I18N(msg) msg
|
|
||||||
#define I18S(msg) msg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void _setupEndpoint(AQHOME_STORAGE *aqh, const char *tcpAddress, int tcpPort);
|
|
||||||
static int _setupHttpService(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
static int _createContentRoot(AQHOME_STORAGE *aqh);
|
|
||||||
static GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data);
|
|
||||||
|
|
||||||
static int _createUrlHandler_login(AQHOME_STORAGE *aqh);
|
|
||||||
static int _createUrlHandler_rooms(AQHOME_STORAGE *aqh);
|
|
||||||
static int _createUrlHandler_devices(AQHOME_STORAGE *aqh);
|
|
||||||
static int _createUrlHandler_topics(AQHOME_STORAGE *aqh);
|
|
||||||
static int _createUrlHandler_values(AQHOME_STORAGE *aqh);
|
|
||||||
static int _createUrlHandler_static(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
int AqHomeStorage_SetupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *tcpAddress;
|
|
||||||
int tcpPort;
|
|
||||||
|
|
||||||
tcpAddress=GWEN_DB_GetCharValue(dbArgs, "httpAddress", 0, NULL);
|
|
||||||
tcpPort=GWEN_DB_GetIntValue(dbArgs, "httpPort", 0, AQHOME_STORAGE_DEFAULT_HTTP_PORT);
|
|
||||||
|
|
||||||
if (tcpAddress && *tcpAddress && tcpPort) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
_setupEndpoint(aqh, tcpAddress, tcpPort);
|
|
||||||
rv=_setupHttpService(aqh, dbArgs);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createContentRoot(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createUrlHandler_login(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createUrlHandler_rooms(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createUrlHandler_devices(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rv=_createUrlHandler_topics(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createUrlHandler_values(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_createUrlHandler_static(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setupEndpoint(AQHOME_STORAGE *aqh, const char *tcpAddress, int tcpPort)
|
|
||||||
{
|
|
||||||
GWEN_MSG_ENDPOINT *ep;
|
|
||||||
|
|
||||||
ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0);
|
|
||||||
GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptHttpFn, aqh);
|
|
||||||
|
|
||||||
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
|
|
||||||
aqh->httpdEndpoint=ep;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _setupHttpService(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *configFolder;
|
|
||||||
const char *sourceFolder;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
configFolder=GWEN_DB_GetCharValue(dbArgs, "cfgdir", 0, AQHOME_STORAGE_DEFAULT_CONFIGDIR);
|
|
||||||
sourceFolder=GWEN_DB_GetCharValue(dbArgs, "sourceFolder", 0, AQHOME_STORAGE_DEFAULT_HTTP_SOURCEDIR);
|
|
||||||
aqh->httpService=AQH_HttpService_new(configFolder, sourceFolder);
|
|
||||||
AqHomeHttpService_Extend(aqh->httpService);
|
|
||||||
AqHomeHttpService_SetStorage(aqh->httpService, aqh->storage);
|
|
||||||
rv=AQH_HttpService_LoadConfig(aqh->httpService);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error loading config for HTTP service (%d)", rv);
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
AQH_HttpService_LoadAllSessions(aqh->httpService);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG_ENDPOINT *_acceptHttpFn(GWEN_MSG_ENDPOINT *ep,
|
|
||||||
GWEN_SOCKET *sk,
|
|
||||||
const GWEN_INETADDRESS *addr,
|
|
||||||
GWEN_UNUSED void *data)
|
|
||||||
{
|
|
||||||
GWEN_MSG_ENDPOINT *epIncoming;
|
|
||||||
|
|
||||||
/* AQHOME_STORAGE *aqh;
|
|
||||||
*
|
|
||||||
* aqh=(AQHOME_STORAGE*) data;
|
|
||||||
*/
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Incoming HTTP connection");
|
|
||||||
epIncoming=GWEN_MsgEndpoint_new("http", 0);
|
|
||||||
GWEN_MsgEndpoint_SetSocket(epIncoming, sk);
|
|
||||||
GWEN_MsgIoEndpoint_Extend(epIncoming);
|
|
||||||
AQH_HttpEndpoint_Extend(epIncoming, AQH_ENDPOINT_HTTP_FLAGS_PASSIVE);
|
|
||||||
return epIncoming;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createContentRoot(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
const char *sourceFolder;
|
|
||||||
GWEN_BUFFER *nbuf1;
|
|
||||||
GWEN_BUFFER *nbuf2;
|
|
||||||
AQH_HTTP_CONTENT *c;
|
|
||||||
|
|
||||||
sourceFolder=AQH_HttpService_GetSourceFolder(aqh->httpService);
|
|
||||||
|
|
||||||
nbuf1=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
nbuf2=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendString(nbuf1, sourceFolder);
|
|
||||||
GWEN_Buffer_AppendString(nbuf1, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_SITEHEADER);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendString(nbuf2, sourceFolder);
|
|
||||||
GWEN_Buffer_AppendString(nbuf2, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_SITEFOOTER);
|
|
||||||
|
|
||||||
c=AQH_HttpContentFiles_new("root", GWEN_Buffer_GetStart(nbuf1), GWEN_Buffer_GetStart(nbuf2));
|
|
||||||
if (c==NULL) {
|
|
||||||
DBG_INFO(NULL,
|
|
||||||
"Unable to create content for root (header=[%s], footer=[%s])",
|
|
||||||
GWEN_Buffer_GetStart(nbuf1), GWEN_Buffer_GetStart(nbuf2));
|
|
||||||
GWEN_Buffer_free(nbuf2);
|
|
||||||
GWEN_Buffer_free(nbuf1);
|
|
||||||
return GWEN_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
AqHomeHttpService_SetContentTree(aqh->httpService, c);
|
|
||||||
|
|
||||||
GWEN_Buffer_free(nbuf2);
|
|
||||||
GWEN_Buffer_free(nbuf1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_login(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_LoginHttpUrlHandler_new(aqh->httpService);
|
|
||||||
AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService));
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/login");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_rooms(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_RoomsHttpUrlHandler_new(aqh->httpService);
|
|
||||||
AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService));
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/rooms/*");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_devices(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_DevicesHttpUrlHandler_new(aqh->httpService);
|
|
||||||
AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService));
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/devices/*");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_topics(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_MqttTopicsHttpUrlHandler_new(aqh->httpService);
|
|
||||||
AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService));
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/mqtttopics/*");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_values(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_ValuesHttpUrlHandler_new(aqh->httpService);
|
|
||||||
AQH_HttpUrlHandler_SetContentProvider(uh, AqHomeHttpService_GetContentTree(aqh->httpService));
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/values/*");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _createUrlHandler_static(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
GWEN_BUFFER *nameBuf;
|
|
||||||
const char *sourceFolder;
|
|
||||||
|
|
||||||
sourceFolder=AQH_HttpService_GetSourceFolder(aqh->httpService);
|
|
||||||
nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, sourceFolder);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQHOME_STORAGE_STATIC_RELFOLDER GWEN_DIR_SEPARATOR_S "pics");
|
|
||||||
|
|
||||||
uh=AQH_StaticHttpUrlHandler_new(aqh->httpService, GWEN_Buffer_GetStart(nameBuf));
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
AQH_HttpUrlHandler_AddUrlPattern(uh, "/pics/*");
|
|
||||||
AQH_HttpService_AddUrlHandler(aqh->httpService, uh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_INIT_HTTP_H
|
|
||||||
#define AQHOME_STORAGE_INIT_HTTP_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_SetupHttp(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./init_mqtt.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "./u_login.h"
|
|
||||||
#include "./u_rooms.h"
|
|
||||||
#include "./u_devices.h"
|
|
||||||
#include "./u_mqtttopics.h"
|
|
||||||
#include "./u_values.h"
|
|
||||||
#include "./u_static.h"
|
|
||||||
|
|
||||||
#include "aqhome/msg/endpoint_tty.h"
|
|
||||||
#include "aqhome/ipc/endpoint_ipc.h"
|
|
||||||
#include "aqhome/mqtt/endpoint_mqttc.h"
|
|
||||||
#include <aqhome/mqtt/msg_mqtt_subscribe.h>
|
|
||||||
#include <aqhome/mqtt/msg_mqtt_suback.h>
|
|
||||||
#include "aqhome/http/endpoint_http.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
#include "aqhome/http/content_files.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint_tcpd.h>
|
|
||||||
#include <gwenhywfar/endpoint_msgio.h>
|
|
||||||
#include <gwenhywfar/directory.h>
|
|
||||||
#include <gwenhywfar/text.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
|
||||||
# include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
# include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define I18N(msg) msg
|
|
||||||
#define I18S(msg) msg
|
|
||||||
|
|
||||||
#define AQHOME_STORAGE_DEFAULT_CMDTIMEOUT 10000
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _mqttConnect(GWEN_MSG_ENDPOINT *epTcp);
|
|
||||||
static int _subscribe(AQHOME_STORAGE *aqh, const char *topicFilter);
|
|
||||||
static GWEN_MSG *_awaitPacket(GWEN_MSG_ENDPOINT *epTcp, uint8_t expectedPacketType, int timeoutInSeconds);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_SetupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs)
|
|
||||||
{
|
|
||||||
const char *mqttAddress;
|
|
||||||
int mqttPort;
|
|
||||||
const char *mqttClientId;
|
|
||||||
int mqttKeepAlive;
|
|
||||||
|
|
||||||
mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL);
|
|
||||||
mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, AQHOME_STORAGE_DEFAULT_MQTT_PORT);
|
|
||||||
mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, AQHOME_STORAGE_DEFAULT_MQTT_CLIENTID);
|
|
||||||
mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, AQHOME_STORAGE_DEFAULT_MQTT_KEEPALIVE);
|
|
||||||
|
|
||||||
if (mqttAddress && *mqttAddress && mqttPort) {
|
|
||||||
GWEN_MSG_ENDPOINT *ep;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
ep=AQH_MqttClientEndpoint_new(mqttClientId, mqttAddress, mqttPort, NULL, 0);
|
|
||||||
AQH_MqttClientEndpoint_SetKeepAliveTime(ep, mqttKeepAlive);
|
|
||||||
|
|
||||||
GWEN_MsgEndpoint_Tree2_AddChild(aqh->rootEndpoint, ep);
|
|
||||||
aqh->mqttEndpoint=ep;
|
|
||||||
|
|
||||||
rv=_mqttConnect(ep);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error connecting to MQTT server %s:%d (%d)", mqttAddress, mqttPort, rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_subscribe(aqh, "#");
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error subscribingconnecting to MQTT server %s:%d (%d)", mqttAddress, mqttPort, rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _mqttConnect(GWEN_MSG_ENDPOINT *epTcp)
|
|
||||||
{
|
|
||||||
if (GWEN_MsgEndpoint_GetState(epTcp)==GWEN_MSG_ENDPOINT_STATE_UNCONNECTED) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=AQH_MqttClientEndpoint_StartConnect(epTcp);
|
|
||||||
if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) {
|
|
||||||
DBG_ERROR(NULL, "Error starting to connect (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(GWEN_MsgEndpoint_GetState(epTcp)!=GWEN_MSG_ENDPOINT_STATE_CONNECTED) {
|
|
||||||
DBG_DEBUG(NULL, "Next loop");
|
|
||||||
GWEN_MsgEndpoint_IoLoop(epTcp, 2000); /* 2000 ms */
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _subscribe(AQHOME_STORAGE *aqh, const char *topicFilter)
|
|
||||||
{
|
|
||||||
uint16_t pckId;
|
|
||||||
GWEN_MSG *msgOut;
|
|
||||||
GWEN_MSG *msgIn;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Sending SUBSCRIBE %s", topicFilter);
|
|
||||||
pckId=AQH_MqttClientEndpoint_GetNextPacketId(aqh->mqttEndpoint);
|
|
||||||
msgOut=GWEN_SubscribeMqttMsg_new(AQH_MQTTMSG_MSGTYPE_SUBSCRIBE, pckId, topicFilter, 0);
|
|
||||||
if (msgOut==NULL) {
|
|
||||||
DBG_ERROR(NULL, "Error creating message");
|
|
||||||
return GWEN_ERROR_INTERNAL;
|
|
||||||
}
|
|
||||||
GWEN_MsgEndpoint_AddSendMessage(aqh->mqttEndpoint, msgOut);
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Waiting for response");
|
|
||||||
msgIn=_awaitPacket(aqh->mqttEndpoint, AQH_MQTTMSG_MSGTYPE_SUBACK, AQHOME_STORAGE_DEFAULT_CMDTIMEOUT);
|
|
||||||
if (msgIn) {
|
|
||||||
GWEN_BUFFER *buf;
|
|
||||||
|
|
||||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
AQH_SubAckMqttMsg_DumpToBuffer(msgIn, buf, "received");
|
|
||||||
DBG_INFO(NULL, "%s", GWEN_Buffer_GetStart(buf));
|
|
||||||
GWEN_Buffer_free(buf);
|
|
||||||
GWEN_Msg_free(msgIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_awaitPacket(GWEN_MSG_ENDPOINT *epTcp, uint8_t expectedPacketType, int timeoutInSeconds)
|
|
||||||
{
|
|
||||||
time_t startTime;
|
|
||||||
|
|
||||||
startTime=time(NULL);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
GWEN_MSG *msg;
|
|
||||||
time_t now;
|
|
||||||
|
|
||||||
GWEN_MsgEndpoint_IoLoop(epTcp, 2000); /* 2000 ms */
|
|
||||||
msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(epTcp);
|
|
||||||
if (msg) {
|
|
||||||
if ((AQH_MqttMsg_GetMsgTypeAndFlags(msg) & 0xf0)==(expectedPacketType & 0xf0)) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Received this message:");
|
|
||||||
GWEN_Text_DumpString((const char*) GWEN_Msg_GetConstBuffer(msg), GWEN_Msg_GetBytesInBuffer(msg), 2);
|
|
||||||
}
|
|
||||||
GWEN_Msg_free(msg);
|
|
||||||
}
|
|
||||||
now=time(NULL);
|
|
||||||
if (now-startTime>timeoutInSeconds) {
|
|
||||||
DBG_INFO(NULL, "Timeout");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_INIT_MQTT_H
|
|
||||||
#define AQHOME_STORAGE_INIT_MQTT_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_SetupMqtt(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./loop.h"
|
|
||||||
#include "./loop_http.h"
|
|
||||||
#include "./loop_mqtt.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _writeModifiedSessions(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs)
|
|
||||||
{
|
|
||||||
if (aqh) {
|
|
||||||
GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs);
|
|
||||||
AqHomeStorage_ReadAndHandleHttpMessages(aqh);
|
|
||||||
AqHomeStorage_ReadAndHandleMqttMessages(aqh);
|
|
||||||
|
|
||||||
// AqHomeStorage_ReadAndHandleIpcMessages(aqh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_WriteStorageIfChanged(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
if (AQH_Storage_GetRuntimeFlags(aqh->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Storage modified, writing statefile");
|
|
||||||
rv=AqHomeHttpService_LockStorage(aqh->httpService);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error locking storage (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
rv=AQH_Storage_WriteState(aqh->storage);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error writing state file (%d)", rv);
|
|
||||||
AqHomeHttpService_UnlockStorage(aqh->httpService);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_UnlockStorage(aqh->httpService);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error unlocking storage (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_WriteServiceIfChanged(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=_writeModifiedSessions(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeModifiedSessions(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
AQH_SESSION_LIST *sessionList;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=AQH_HttpService_LockSessions(aqh->httpService);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error locking sessions (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionList=AQH_Service_GetSessionList(aqh->httpService);
|
|
||||||
if (sessionList) {
|
|
||||||
AQH_SESSION *session;
|
|
||||||
|
|
||||||
session=AQH_Session_List_First(sessionList);
|
|
||||||
while(session) {
|
|
||||||
if (AQH_Session_GetRuntimeFlags(session) & AQH_SESSION_RTFLAGS_MODIFIED) {
|
|
||||||
DBG_INFO(NULL, "Session \"%s\" modified, writing", AQH_Session_GetUid(session));
|
|
||||||
rv=AQH_HttpService_SaveSession(aqh->httpService, session);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error writing session \"%s\" (%d)", AQH_Session_GetUid(session), rv);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_DEBUG(NULL, "Session \"%s\" written", AQH_Session_GetUid(session));
|
|
||||||
AQH_Session_SubRuntimeFlags(session, AQH_SESSION_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session=AQH_Session_List_Next(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AQH_HttpService_UnlockSessions(aqh->httpService);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error unlocking sessions (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_LOOP_H
|
|
||||||
#define AQHOME_STORAGE_LOOP_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs);
|
|
||||||
|
|
||||||
int AqHomeStorage_WriteStorageIfChanged(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
int AqHomeStorage_WriteServiceIfChanged(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./loop_http.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
|
|
||||||
#include "aqhome/msg/endpoint_tty.h"
|
|
||||||
#include "aqhome/ipc/endpoint_ipc.h"
|
|
||||||
#include "aqhome/mqtt/endpoint_mqttc.h"
|
|
||||||
#include "aqhome/http/endpoint_http.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/text.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint_tcpd.h>
|
|
||||||
#include <gwenhywfar/endpoint_msgio.h>
|
|
||||||
#include <gwenhywfar/syncio.h>
|
|
||||||
#include <gwenhywfar/url.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
|
||||||
# include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
# include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define I18N(msg) msg
|
|
||||||
#define I18S(msg) msg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void _handleHttpEndpoint(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep);
|
|
||||||
static void _handleHttpMsg(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
void AqHomeStorage_ReadAndHandleHttpMessages(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
if (aqh->httpdEndpoint) {
|
|
||||||
GWEN_MSG_ENDPOINT *ep;
|
|
||||||
|
|
||||||
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->httpdEndpoint);
|
|
||||||
while(ep) {
|
|
||||||
_handleHttpEndpoint(aqh, ep);
|
|
||||||
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleHttpEndpoint(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep)
|
|
||||||
{
|
|
||||||
GWEN_MSG *msg;
|
|
||||||
|
|
||||||
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) {
|
|
||||||
_handleHttpMsg(aqh, ep, msg);
|
|
||||||
GWEN_Msg_free(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleHttpMsg(AQHOME_STORAGE *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msgReceived)
|
|
||||||
{
|
|
||||||
GWEN_MSG *msgResponse;
|
|
||||||
|
|
||||||
msgResponse=AQH_HttpService_HandleHttpRequest(aqh->httpService, ep, msgReceived);
|
|
||||||
if (msgResponse) {
|
|
||||||
DBG_INFO(NULL, "Sending response message");
|
|
||||||
GWEN_MsgEndpoint_AddSendMessage(ep, msgResponse);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No response for message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./loop_mqtt.h"
|
|
||||||
#include "./aqhomestorage_p.h"
|
|
||||||
|
|
||||||
#include "aqhome/mqtt/msg_mqtt_publish.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define I18N(msg) msg
|
|
||||||
#define I18S(msg) msg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void _handlePublishMsg(AQHOME_STORAGE *aqh, GWEN_MSG *msg);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_ReadAndHandleMqttMessages(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
GWEN_MSG *msg;
|
|
||||||
|
|
||||||
while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(aqh->mqttEndpoint)) ) {
|
|
||||||
if ((AQH_MqttMsg_GetMsgTypeAndFlags(msg) & 0xf0)==(AQH_MQTTMSG_MSGTYPE_PUBLISH & 0xf0)) {
|
|
||||||
_handlePublishMsg(aqh, msg);
|
|
||||||
}
|
|
||||||
else if ((AQH_MqttMsg_GetMsgTypeAndFlags(msg) & 0xf0)==(AQH_MQTTMSG_MSGTYPE_PINGRESP & 0xf0)) {
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "PING response received");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "Received unexpected MQTT message %02x", AQH_MqttMsg_GetMsgTypeAndFlags(msg));
|
|
||||||
}
|
|
||||||
GWEN_Msg_free(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int AqHomeStorage_MqttPing(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
GWEN_MSG *msgOut;
|
|
||||||
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "Sending PING");
|
|
||||||
msgOut=GWEN_MqttMsg_new(AQH_MQTTMSG_MSGTYPE_PINGREQ, 0, NULL);
|
|
||||||
if (msgOut==NULL) {
|
|
||||||
DBG_ERROR(NULL, "Error creating message");
|
|
||||||
return GWEN_ERROR_INTERNAL;
|
|
||||||
}
|
|
||||||
GWEN_MsgEndpoint_AddSendMessage(aqh->mqttEndpoint, msgOut);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handlePublishMsg(AQHOME_STORAGE *aqh, GWEN_MSG *msg)
|
|
||||||
{
|
|
||||||
char *topic;
|
|
||||||
char *value;
|
|
||||||
|
|
||||||
topic=AQH_PublishMqttMsg_ExtractTopic(msg);
|
|
||||||
value=AQH_PublishMqttMsg_ExtractValue(msg);
|
|
||||||
|
|
||||||
if (topic && value)
|
|
||||||
AQH_Storage_HandleMqttPublish(aqh->storage, topic, value);
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Either topic or value missing in PUBLISH msg");
|
|
||||||
}
|
|
||||||
free(value);
|
|
||||||
free(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_MQTT_H
|
|
||||||
#define AQHOME_STORAGE_MQTT_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_ReadAndHandleMqttMessages(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
int AqHomeStorage_MqttPing(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <aqhome/api.h>
|
|
||||||
#include <aqhome/aqhome.h>
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
|
||||||
#include "./init.h"
|
|
||||||
#include "./fini.h"
|
|
||||||
#include "./loop.h"
|
|
||||||
#include "./loop_mqtt.h"
|
|
||||||
#include "./cleanup.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/logger.h>
|
|
||||||
#include <gwenhywfar/cgui.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SIGNAL_H
|
|
||||||
# include <signal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//#define CLEANUP_INTERVAL_IN_SECS (5*60)
|
|
||||||
//#define WRITE_INTERVAL_IN_SECS (5*60)
|
|
||||||
|
|
||||||
#define CLEANUP_INTERVAL_IN_SECS (60)
|
|
||||||
#define WRITE_INTERVAL_IN_SECS (60)
|
|
||||||
#define PING_INTERVAL 120
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_SIGNAL_H
|
|
||||||
static int _setSignalHandlers(void);
|
|
||||||
static int _setupSigAction(struct sigaction *sa, int sig);
|
|
||||||
static void _signalHandler(int s);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void _runService(AQHOME_STORAGE *aqh);
|
|
||||||
static void _writeCurrentState(AQHOME_STORAGE *aqh);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* static vars
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_SIGNAL_H
|
|
||||||
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int stopService=0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
AQHOME_STORAGE *aqh;
|
|
||||||
GWEN_GUI *gui;
|
|
||||||
|
|
||||||
rv=GWEN_Init();
|
|
||||||
if (rv) {
|
|
||||||
fprintf(stderr, "ERROR: Unable to init Gwen.\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
GWEN_Logger_Open(0, "aqhome-storage", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
|
|
||||||
//GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
|
|
||||||
GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info);
|
|
||||||
|
|
||||||
rv=_setSignalHandlers();
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AQH_Init();
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui=GWEN_Gui_CGui_new();
|
|
||||||
GWEN_Gui_SetGui(gui);
|
|
||||||
|
|
||||||
aqh=AqHomeStorage_new();
|
|
||||||
rv=AqHomeStorage_Init(aqh, argc, argv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
_runService(aqh);
|
|
||||||
|
|
||||||
AqHomeStorage_Fini(aqh);
|
|
||||||
AqHomeStorage_free(aqh);
|
|
||||||
|
|
||||||
GWEN_Gui_SetGui(NULL);
|
|
||||||
GWEN_Gui_free(gui);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _runService(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
time_t timeStart;
|
|
||||||
time_t timeLastCleanup;
|
|
||||||
time_t timeLastWrite;
|
|
||||||
time_t timeLastPing;
|
|
||||||
int timeout;
|
|
||||||
|
|
||||||
timeout=AqHomeStorage_GetTimeout(aqh);
|
|
||||||
timeStart=time(NULL);
|
|
||||||
timeLastCleanup=time(NULL);
|
|
||||||
timeLastWrite=time(NULL);
|
|
||||||
timeLastPing=time(NULL);
|
|
||||||
|
|
||||||
while(!stopService) {
|
|
||||||
time_t now;
|
|
||||||
|
|
||||||
DBG_DEBUG(NULL, "Next loop");
|
|
||||||
AqHomeStorage_Loop(aqh, 2000);
|
|
||||||
|
|
||||||
now=time(NULL);
|
|
||||||
|
|
||||||
if (((int)difftime(now, timeLastCleanup))>CLEANUP_INTERVAL_IN_SECS) {
|
|
||||||
DBG_INFO(NULL, "Cleanup time");
|
|
||||||
AqHomeStorage_Cleanup(aqh);
|
|
||||||
timeLastCleanup=now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((int)difftime(now, timeLastWrite))>WRITE_INTERVAL_IN_SECS) {
|
|
||||||
DBG_INFO(NULL, "Write time");
|
|
||||||
_writeCurrentState(aqh);
|
|
||||||
timeLastWrite=now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout && ((int)difftime(now, timeLastPing))>timeout) {
|
|
||||||
DBG_INFO(NULL, "Sending ping");
|
|
||||||
AqHomeStorage_MqttPing(aqh);
|
|
||||||
timeLastPing=now;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (timeout && ((int)difftime(now, timeStart))>timeout) {
|
|
||||||
DBG_INFO(NULL, "Timeout");
|
|
||||||
_writeCurrentState(aqh);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} /* while */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _writeCurrentState(AQHOME_STORAGE *aqh)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=AqHomeStorage_WriteStorageIfChanged(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "ATTENTION: Could not write storage statefile (%d)", rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeStorage_WriteServiceIfChanged(aqh);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "ATTENTION: Could not write current config (%d)", rv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _setSignalHandlers(void)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_SIGNAL_H
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=_setupSigAction(&saINT, SIGINT);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
rv=_setupSigAction(&saTERM, SIGTERM);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
rv=_setupSigAction(&saHUP, SIGHUP);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
# ifdef SIGTSTP
|
|
||||||
rv=_setupSigAction(&saTSTP, SIGTSTP);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef SIGCONT
|
|
||||||
rv=_setupSigAction(&saCONT, SIGCONT);
|
|
||||||
if (rv)
|
|
||||||
return rv;
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _setupSigAction(struct sigaction *sa, int sig)
|
|
||||||
{
|
|
||||||
sa->sa_handler=_signalHandler;
|
|
||||||
sigemptyset(&sa->sa_mask);
|
|
||||||
sa->sa_flags=0;
|
|
||||||
if (sigaction(sig, sa, 0)) {
|
|
||||||
DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig);
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _signalHandler(int s)
|
|
||||||
{
|
|
||||||
switch(s) {
|
|
||||||
case SIGINT:
|
|
||||||
case SIGTERM:
|
|
||||||
case SIGHUP:
|
|
||||||
DBG_WARN(0, "Received signal %d, stopping service in next loop.",s);
|
|
||||||
stopService=1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DBG_WARN(0, "Unknown signal %d",s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_base.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *AQH_BaseHttpUrlHandler_CreateResponseForErrorCode(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_HTTP_REQUEST *rq,
|
|
||||||
int rv,
|
|
||||||
AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb,
|
|
||||||
GWEN_DB_NODE *db)
|
|
||||||
{
|
|
||||||
if (rv<0) {
|
|
||||||
switch(rv) {
|
|
||||||
case GWEN_ERROR_INVALID:
|
|
||||||
return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Missing fields"), 1, db, cb);
|
|
||||||
case GWEN_ERROR_FOUND:
|
|
||||||
return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Object already exists"), 1, db, cb);
|
|
||||||
case GWEN_ERROR_NOT_FOUND:
|
|
||||||
return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Object not found"), 1, db, cb);
|
|
||||||
case GWEN_ERROR_IO:
|
|
||||||
case GWEN_ERROR_INTERNAL:
|
|
||||||
default:
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh),
|
|
||||||
500, "Internal error",
|
|
||||||
AQH_HttpRequest_GetProtocol(rq), NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_BASE_H
|
|
||||||
#define AQHOME_STORAGE_U_BASE_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *AQH_BaseHttpUrlHandler_CreateResponseForErrorCode(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_HTTP_REQUEST *rq,
|
|
||||||
int rv,
|
|
||||||
AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb,
|
|
||||||
GWEN_DB_NODE *db);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_devices.h"
|
|
||||||
#include "./u_objects.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
static void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _setFromObject(AQH_DEVICE *device, const AQH_DEVICE *srcDevice);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_DevicesHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_ObjectsHttpUrlHandler_new(sv,
|
|
||||||
AQHOME_HTTP_PERMS_LIST_DEVICES,
|
|
||||||
AQHOME_HTTP_PERMS_ADD_DEVICE,
|
|
||||||
AQHOME_HTTP_PERMS_DEL_DEVICE,
|
|
||||||
AQHOME_HTTP_PERMS_EDIT_DEVICE,
|
|
||||||
"/devices/list");
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(uh, _addOrEditObject);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(uh, _findObjectByIdAndReturnAsDb);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(uh, _writeAddPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(uh, _writeEditPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(uh, _listObjectsIntoBuffer);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
AQH_DEVICE *newDevice;
|
|
||||||
const char *deviceName;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
newDevice=AQH_Device_fromDb(db);
|
|
||||||
|
|
||||||
deviceName=AQH_Device_GetName(newDevice);
|
|
||||||
if (!(deviceName && *deviceName)) {
|
|
||||||
DBG_INFO(NULL, "Missing device name");
|
|
||||||
AQH_Device_free(newDevice);
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_LockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error locking storage");
|
|
||||||
AQH_Device_free(newDevice);
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id>0) {
|
|
||||||
AQH_DEVICE *device;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Edit existing device");
|
|
||||||
device=AQH_Storage_GetDeviceById(sto, id);
|
|
||||||
if (device==NULL) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Device %d not found", id);
|
|
||||||
AQH_Device_free(newDevice);
|
|
||||||
return GWEN_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
AQH_Device_SetId(device, id);
|
|
||||||
_setFromObject(device, newDevice);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AQH_DEVICE *device;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Adding new device");
|
|
||||||
device=AQH_Storage_GetDeviceByName(sto, deviceName);
|
|
||||||
if (device) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Device %s exists", deviceName);
|
|
||||||
AQH_Device_free(newDevice);
|
|
||||||
return GWEN_ERROR_FOUND;
|
|
||||||
}
|
|
||||||
device=AQH_Device_new();
|
|
||||||
AQH_Device_SetId(device, 0);
|
|
||||||
_setFromObject(device, newDevice);
|
|
||||||
AQH_Storage_AddDevice(sto, device);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
AQH_Device_free(newDevice);
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error unlocking storage");
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setFromObject(AQH_DEVICE *device, const AQH_DEVICE *srcDevice)
|
|
||||||
{
|
|
||||||
AQH_Device_SetRoomId(device, AQH_Device_GetRoomId(srcDevice));
|
|
||||||
AQH_Device_SetName(device, AQH_Device_GetName(srcDevice));
|
|
||||||
AQH_Device_SetDeviceType(device, AQH_Device_GetDeviceType(srcDevice));
|
|
||||||
AQH_Device_SetLocation(device, AQH_Device_GetLocation(srcDevice));
|
|
||||||
AQH_Device_SetDescription(device, AQH_Device_GetDescription(srcDevice));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_DEVICE *device;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
device=AQH_Storage_GetDeviceById(sto, id);
|
|
||||||
if (device) {
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
db=GWEN_DB_Group_new("device");
|
|
||||||
AQH_Device_toDb(device, db);
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/devices/add\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Add Device"));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/devices/edit/%lu\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Edit Device"),
|
|
||||||
(long unsigned int)(dbValues?GWEN_DB_GetIntValue(dbValues, "id", 0, 0):0));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_ROOM_LIST *roomList;
|
|
||||||
unsigned long int selectedRoomId=0;
|
|
||||||
char numbuf[16];
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
roomList=AQH_Storage_GetRoomList(sto);
|
|
||||||
|
|
||||||
selectedRoomId=(unsigned long int)(dbValues?GWEN_DB_GetIntValue(dbValues, "roomId", 0, 0):0);
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", selectedRoomId);
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <table>"
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"name\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"name\" value=\"%s\" required></td>"
|
|
||||||
" </tr>",
|
|
||||||
I18N("Name"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "name", 0, ""):"");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td><label for=\"roomId\">%s: </label></td>"
|
|
||||||
"<td><select id=\"roomId\" name=\"roomId\">",
|
|
||||||
I18N("Room"));
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"0\">%s</option>", I18N("-- select room --"));
|
|
||||||
|
|
||||||
if (roomList) {
|
|
||||||
const AQH_ROOM *r;
|
|
||||||
|
|
||||||
r=AQH_Room_List_First(roomList);
|
|
||||||
while(r) {
|
|
||||||
const char *roomName;
|
|
||||||
|
|
||||||
roomName=AQH_Room_GetName(r);
|
|
||||||
if (roomName && *roomName) {
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", (unsigned long int) AQH_Room_GetId(r));
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
if (selectedRoomId && AQH_Room_GetId(r)==selectedRoomId)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\" selected>%s</option>", numbuf, roomName);
|
|
||||||
else
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\">%s</option>", numbuf, roomName);
|
|
||||||
}
|
|
||||||
r=AQH_Room_List_Next(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</select></td></tr>");
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"deviceType\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"deviceType\" value=\"%s\"></td>"
|
|
||||||
" </tr>",
|
|
||||||
I18N("Type"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "deviceType", 0, ""):"");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"location\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"location\" value=\"%s\"></td>"
|
|
||||||
" </tr>",
|
|
||||||
I18N("Location"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "location", 0, ""):"");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"description\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"description\" value=\"%s\"></td>"
|
|
||||||
" </tr>"
|
|
||||||
" </table>",
|
|
||||||
I18N("Description"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "description", 0, ""):"");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_DEVICE_LIST *rl;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2>"
|
|
||||||
"<table class=\"dataTable\">"
|
|
||||||
"<thead>"
|
|
||||||
" <tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th></th></tr>"
|
|
||||||
"</thead>",
|
|
||||||
I18N("Devices"),
|
|
||||||
I18N("Name"),
|
|
||||||
I18N("Room"),
|
|
||||||
I18N("Type"),
|
|
||||||
I18N("Location"),
|
|
||||||
I18N("Description"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "<tbody>");
|
|
||||||
rl=AQH_Storage_GetDeviceList(sto);
|
|
||||||
if (rl) {
|
|
||||||
const AQH_DEVICE *device;
|
|
||||||
|
|
||||||
device=AQH_Device_List_First(rl);
|
|
||||||
while(device) {
|
|
||||||
long unsigned int id;
|
|
||||||
int roomId;
|
|
||||||
const char *name;
|
|
||||||
const char *roomName=NULL;
|
|
||||||
const char *devType;
|
|
||||||
const char *descr;
|
|
||||||
const char *location;
|
|
||||||
const AQH_ROOM *r=NULL;
|
|
||||||
|
|
||||||
id=(long unsigned int) AQH_Device_GetId(device);
|
|
||||||
roomId=(long unsigned int) AQH_Device_GetRoomId(device);
|
|
||||||
if (roomId>0)
|
|
||||||
r=AQH_Storage_GetRoomById(sto, roomId);
|
|
||||||
if (r)
|
|
||||||
roomName=AQH_Room_GetName(r);
|
|
||||||
|
|
||||||
name=AQH_Device_GetName(device);
|
|
||||||
devType=AQH_Device_GetDeviceType(device);
|
|
||||||
descr=AQH_Device_GetDescription(device);
|
|
||||||
location=AQH_Device_GetLocation(device);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
|
|
||||||
"<td><a href=\"/devices/edit/%lu\">"
|
|
||||||
"<IMG src=\"/pics/edit.png\" width=32 height=32 align=left border=0></a></td>"
|
|
||||||
"</tr>",
|
|
||||||
name?name:"",
|
|
||||||
roomName?roomName:"",
|
|
||||||
devType?devType:"",
|
|
||||||
location?location:"",
|
|
||||||
descr?descr:"", id);
|
|
||||||
|
|
||||||
device=AQH_Device_List_Next(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</tbody>");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "</table><a href=\"/devices/add\">%s</a><br>", I18N("Add Device"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_DEVICES_H
|
|
||||||
#define AQHOME_STORAGE_U_DEVICES_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_DevicesHttpUrlHandler_new(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,394 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_login.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/args.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
#include <gwenhywfar/mdigest.h>
|
|
||||||
#include <gwenhywfar/text.h>
|
|
||||||
#include <gwenhywfar/timestamp.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
|
|
||||||
static int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static int _verifyPass(AQH_USER *u, const char *userName, const char *password);
|
|
||||||
static AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u);
|
|
||||||
static GWEN_BUFFER *_generateSessionUid(void);
|
|
||||||
static void _headerSetCookie(GWEN_DB_NODE *db, uint32_t flags, const char *cookieName, const char *cookieValue);
|
|
||||||
static GWEN_MSG *_createLoginResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *sessionId, const char *newPage);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_HttpUrlHandler_new(sv);
|
|
||||||
AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
const char *protocol;
|
|
||||||
const char *cmd;
|
|
||||||
GWEN_MSG *msgOut=NULL;
|
|
||||||
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
cmd=AQH_HttpRequest_GetCommand(rq);
|
|
||||||
if (cmd && *cmd) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (strcasecmp(cmd, "GET")==0) {
|
|
||||||
rv=_handleGet(uh, rq);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcasecmp(cmd, "POST")==0) {
|
|
||||||
rv=_handlePost(uh, rq);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "No command in request");
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
GWEN_BUFFER *pageBuf;
|
|
||||||
int rv;
|
|
||||||
GWEN_MSG *msgOut=NULL;
|
|
||||||
const char *protocol;
|
|
||||||
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
pageBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
rv=AQH_HttpUrlHandler_AddContentHeaders(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers");
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AQH_HttpService_AddFile(AQH_HttpUrlHandler_GetHttpService(uh), NULL, "login.html", pageBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"login.html\"");
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers");
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf));
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "Login POST:");
|
|
||||||
db=AQH_HttpRequest_GetDbPostBody(rq);
|
|
||||||
GWEN_DB_Dump(db, 2);
|
|
||||||
|
|
||||||
rv=_loginUser(uh, rq);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "here (%d)", rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
GWEN_MSG *msgOut=NULL;
|
|
||||||
const char *protocol;
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
const char *userName;
|
|
||||||
const char *password;
|
|
||||||
AQH_USER *u;
|
|
||||||
AQH_SESSION *session;
|
|
||||||
int i;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
db=AQH_HttpRequest_GetDbPostBody(rq);
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
|
|
||||||
userName=GWEN_DB_GetCharValue(db, "username", 0, NULL);
|
|
||||||
password=GWEN_DB_GetCharValue(db, "password", 0, NULL);
|
|
||||||
if (!(userName && *userName && password && *password)) {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(sv, 401, "Unauthorized", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u=AQH_HttpService_GetUser(sv, userName);
|
|
||||||
if (u==NULL) {
|
|
||||||
DBG_INFO(NULL, "User \"%s\" not found", userName);
|
|
||||||
/* TODO: sleep */
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
i=AQH_User_GetState(u);
|
|
||||||
if (i==AQH_UserState_Suspended) {
|
|
||||||
DBG_INFO(NULL, "User \"%s\" suspended", userName);
|
|
||||||
/* TODO: sleep */
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=_verifyPass(u, userName, password);
|
|
||||||
if (rv<0) {
|
|
||||||
if (rv==GWEN_ERROR_VERIFY)
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, "error");
|
|
||||||
else
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
/* TODO: sleep */
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
session=_generateSessionForUser(sv, u);
|
|
||||||
if (session==NULL) {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
/* TODO: sleep */
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "Session generated");
|
|
||||||
AQH_User_SetTimestampLastLogin(u, AQH_Session_GetTimestampCreation(session));
|
|
||||||
rv=AQH_HttpService_WriteUser(sv, u);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
msgOut=_createLoginResponseMsg(sv, protocol, AQH_Session_GetUid(session), "/rooms/list");
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _verifyPass(AQH_USER *u, const char *userName, const char *password)
|
|
||||||
{
|
|
||||||
GWEN_MDIGEST *md;
|
|
||||||
int rv;
|
|
||||||
const char *storedPasswordHash;
|
|
||||||
char buffer[70];
|
|
||||||
|
|
||||||
storedPasswordHash=AQH_User_GetHashedPassword(u);
|
|
||||||
if (!(storedPasswordHash && *storedPasswordHash)) {
|
|
||||||
DBG_ERROR(NULL, "No password hash stored with user \"%s\"", userName);
|
|
||||||
return GWEN_ERROR_INTERNAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
md=GWEN_MDigest_Sha256_new();
|
|
||||||
rv=GWEN_MDigest_Begin(md);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error digesting given password [begin] (%d)", rv);
|
|
||||||
GWEN_MDigest_free(md);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=GWEN_MDigest_Update(md, (const uint8_t*) password, strlen(password));
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error digesting given password [update] (%d)", rv);
|
|
||||||
GWEN_MDigest_free(md);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=GWEN_MDigest_End(md);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error digesting given password [end] (%d)", rv);
|
|
||||||
GWEN_MDigest_free(md);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "Digest needs %d bytes", (GWEN_MDigest_GetDigestSize(md)*2)+1);
|
|
||||||
if (NULL==GWEN_Text_ToHex((const char*) GWEN_MDigest_GetDigestPtr(md), GWEN_MDigest_GetDigestSize(md), buffer, sizeof(buffer)-1)) {
|
|
||||||
DBG_ERROR(NULL, "Buffer too small (need %d)", (GWEN_MDigest_GetDigestSize(md)*2)+1);
|
|
||||||
GWEN_MDigest_free(md);
|
|
||||||
return GWEN_ERROR_INTERNAL;
|
|
||||||
}
|
|
||||||
GWEN_MDigest_free(md);
|
|
||||||
|
|
||||||
if (strcasecmp(buffer, storedPasswordHash)!=0) {
|
|
||||||
DBG_ERROR(NULL, "Bad password for user \"%s\"", userName);
|
|
||||||
return GWEN_ERROR_VERIFY;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "Valid password for user \"%s\"", userName);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u)
|
|
||||||
{
|
|
||||||
AQH_SESSION *session;
|
|
||||||
GWEN_BUFFER *buf;
|
|
||||||
GWEN_TIMESTAMP *ts;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
buf=_generateSessionUid();
|
|
||||||
if (buf==NULL) {
|
|
||||||
DBG_INFO(NULL, "here");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
DBG_INFO(NULL, "New session id: [%s]", GWEN_Buffer_GetStart(buf));
|
|
||||||
|
|
||||||
session=AQH_Session_new();
|
|
||||||
AQH_Session_SetUid(session, GWEN_Buffer_GetStart(buf));
|
|
||||||
GWEN_Buffer_free(buf);
|
|
||||||
|
|
||||||
ts=GWEN_Timestamp_NowInLocalTime();
|
|
||||||
AQH_Session_SetTimestampCreation(session, ts);
|
|
||||||
AQH_Session_SetTimestampLastAccess(session, ts);
|
|
||||||
GWEN_Timestamp_free(ts);
|
|
||||||
|
|
||||||
AQH_Session_SetUserAlias(session, AQH_User_GetAlias(u));
|
|
||||||
AQH_Session_SetUser(session, u);
|
|
||||||
|
|
||||||
rv=AQH_HttpService_AddSession(sv, session);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_INFO(NULL, "Error adding session \"%s\" (%d)", AQH_Session_GetUid(session), rv);
|
|
||||||
AQH_Session_free(session);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_BUFFER *_generateSessionUid(void)
|
|
||||||
{
|
|
||||||
uint8_t binbuf[8];
|
|
||||||
GWEN_BUFFER *buf;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv=GWEN_SyncIo_Helper_PartiallyReadFile("/dev/urandom", binbuf, sizeof(binbuf));
|
|
||||||
if (rv<sizeof(binbuf)) {
|
|
||||||
DBG_ERROR(NULL, "Error reading from /dev/urandom: %d", rv);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf=GWEN_Buffer_new(0, 20, 0, 1);
|
|
||||||
rv=GWEN_Text_ToHexBuffer((const char*) binbuf, rv, buf, 0, 0, 0);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error converting random bytes to hex (%d)", rv);
|
|
||||||
GWEN_Buffer_free(buf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_createLoginResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *sessionId, const char *newPage)
|
|
||||||
{
|
|
||||||
GWEN_BUFFER *buf;
|
|
||||||
GWEN_MSG *msg;
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
AQH_HttpService_AddStatusLine(sv, 303, "OK, redirecting to content", protocol, buf);
|
|
||||||
db=GWEN_DB_Group_new("header");
|
|
||||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Connection", "Keep-Alive");
|
|
||||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Type", "text/html; charset=utf-8");
|
|
||||||
_headerSetCookie(db, GWEN_DB_FLAGS_OVERWRITE_VARS, AQH_HTTP_REQUEST_SESSIONCOOKIE, sessionId);
|
|
||||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Location", newPage);
|
|
||||||
GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", 0);
|
|
||||||
AQH_HttpService_AddHeader(sv, db, buf);
|
|
||||||
GWEN_DB_Group_free(db);
|
|
||||||
|
|
||||||
msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
|
|
||||||
GWEN_Buffer_free(buf);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _headerSetCookie(GWEN_DB_NODE *db, uint32_t flags, const char *cookieName, const char *cookieValue)
|
|
||||||
{
|
|
||||||
if (cookieName && cookieValue) {
|
|
||||||
GWEN_BUFFER *dbuf;
|
|
||||||
|
|
||||||
dbuf=GWEN_Buffer_new(0, 32, 0, 1);
|
|
||||||
GWEN_Buffer_AppendArgs(dbuf, "%s=%s", cookieName, cookieValue);
|
|
||||||
GWEN_DB_SetCharValue(db, flags, "Set-Cookie", GWEN_Buffer_GetStart(dbuf));
|
|
||||||
GWEN_Buffer_free(dbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_LOGIN_H
|
|
||||||
#define AQHOME_STORAGE_U_LOGIN_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_mqtttopics.h"
|
|
||||||
#include "./u_objects.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
static void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _setFromObject(AQH_MQTT_TOPIC *mqttTopic, const AQH_MQTT_TOPIC *srcMqttTopic);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_MqttTopicsHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_ObjectsHttpUrlHandler_new(sv,
|
|
||||||
AQHOME_HTTP_PERMS_LIST_TOPICS,
|
|
||||||
AQHOME_HTTP_PERMS_ADD_TOPIC,
|
|
||||||
AQHOME_HTTP_PERMS_DEL_TOPIC,
|
|
||||||
AQHOME_HTTP_PERMS_EDIT_TOPIC,
|
|
||||||
"/mqtttopics/list");
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(uh, _addOrEditObject);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(uh, _findObjectByIdAndReturnAsDb);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(uh, _writeAddPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(uh, _writeEditPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(uh, _listObjectsIntoBuffer);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
AQH_MQTT_TOPIC *newMqttTopic;
|
|
||||||
const char *topic;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
newMqttTopic=AQH_MqttTopic_fromDb(db);
|
|
||||||
|
|
||||||
topic=AQH_MqttTopic_GetTopic(newMqttTopic);
|
|
||||||
if (!(topic && *topic)) {
|
|
||||||
DBG_INFO(NULL, "Missing mqttTopic topic");
|
|
||||||
AQH_MqttTopic_free(newMqttTopic);
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_LockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error locking storage");
|
|
||||||
AQH_MqttTopic_free(newMqttTopic);
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id>0) {
|
|
||||||
AQH_MQTT_TOPIC *mqttTopic;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Edit existing mqttTopic");
|
|
||||||
mqttTopic=AQH_Storage_GetMqttTopicById(sto, id);
|
|
||||||
if (mqttTopic==NULL) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "MqttTopic %d not found", id);
|
|
||||||
AQH_MqttTopic_free(newMqttTopic);
|
|
||||||
return GWEN_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
AQH_MqttTopic_SetId(mqttTopic, id);
|
|
||||||
_setFromObject(mqttTopic, newMqttTopic);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AQH_MQTT_TOPIC *mqttTopic;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Adding new mqttTopic");
|
|
||||||
mqttTopic=AQH_Storage_GetMqttTopicByTopic(sto, topic);
|
|
||||||
if (mqttTopic) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "MqttTopic %s exists", topic);
|
|
||||||
AQH_MqttTopic_free(newMqttTopic);
|
|
||||||
return GWEN_ERROR_FOUND;
|
|
||||||
}
|
|
||||||
mqttTopic=AQH_MqttTopic_new();
|
|
||||||
AQH_MqttTopic_SetId(mqttTopic, 0);
|
|
||||||
_setFromObject(mqttTopic, newMqttTopic);
|
|
||||||
AQH_Storage_AddMqttTopic(sto, mqttTopic);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
AQH_MqttTopic_free(newMqttTopic);
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error unlocking storage");
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setFromObject(AQH_MQTT_TOPIC *mqttTopic, const AQH_MQTT_TOPIC *srcMqttTopic)
|
|
||||||
{
|
|
||||||
AQH_MqttTopic_SetDeviceId(mqttTopic, AQH_MqttTopic_GetDeviceId(srcMqttTopic));
|
|
||||||
AQH_MqttTopic_SetTopic(mqttTopic, AQH_MqttTopic_GetTopic(srcMqttTopic));
|
|
||||||
AQH_MqttTopic_SetDataType(mqttTopic, AQH_MqttTopic_GetDataType(srcMqttTopic));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_MQTT_TOPIC *mqttTopic;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
mqttTopic=AQH_Storage_GetMqttTopicById(sto, id);
|
|
||||||
if (mqttTopic) {
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
db=GWEN_DB_Group_new("mqttTopic");
|
|
||||||
AQH_MqttTopic_toDb(mqttTopic, db);
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/mqtttopics/add\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Add MQTT Topic"));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/mqtttopics/edit/%lu\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Edit MQTT Topic"),
|
|
||||||
(long unsigned int)(dbValues?GWEN_DB_GetIntValue(dbValues, "id", 0, 0):0));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const GWEN_STRINGLIST *seenTopicsList;
|
|
||||||
const AQH_DEVICE_LIST *deviceList;
|
|
||||||
unsigned long int selectedDeviceId=0;
|
|
||||||
int dataType;
|
|
||||||
char numbuf[16];
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
deviceList=AQH_Storage_GetDeviceList(sto);
|
|
||||||
|
|
||||||
selectedDeviceId=(unsigned long int)(dbValues?GWEN_DB_GetIntValue(dbValues, "deviceId", 0, 0):0);
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", selectedDeviceId);
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
dataType=dbValues?GWEN_DB_GetIntValue(dbValues, "dataType", 0, AQH_MqttTopicType_Num):0;
|
|
||||||
|
|
||||||
/* topic */
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<table>"
|
|
||||||
"<tr>"
|
|
||||||
" <td><label for=\"topic\">%s: </label></td>"
|
|
||||||
" <td>"
|
|
||||||
" <input list=\"seenTopics\" type=\"text\" name=\"topic\" id=\"topic\" value=\"%s\" size=\"64\" required>\n"
|
|
||||||
" <datalist id=\"seenTopics\">",
|
|
||||||
I18N("MQTT Topic"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "topic", 0, ""):"");
|
|
||||||
seenTopicsList=AQH_Storage_GetRecvdTopicList(sto);
|
|
||||||
if (seenTopicsList && GWEN_StringList_Count(seenTopicsList)) {
|
|
||||||
GWEN_STRINGLISTENTRY *se;
|
|
||||||
|
|
||||||
se=GWEN_StringList_FirstEntry(seenTopicsList);
|
|
||||||
while(se) {
|
|
||||||
const char *s=GWEN_StringListEntry_Data(se);
|
|
||||||
if (s && *s) {
|
|
||||||
DBG_INFO(NULL, "Adding MQTT string %s", s);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\">\n", s);
|
|
||||||
}
|
|
||||||
se=GWEN_StringListEntry_Next(se);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</datalist></td></tr>");
|
|
||||||
|
|
||||||
|
|
||||||
/* device */
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td><label for=\"deviceId\">%s: </label></td>"
|
|
||||||
"<td><select id=\"deviceId\" name=\"deviceId\">",
|
|
||||||
I18N("Device"));
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"0\">%s</option>\n", I18N("-- select device --"));
|
|
||||||
|
|
||||||
if (deviceList) {
|
|
||||||
const AQH_DEVICE *device;
|
|
||||||
|
|
||||||
device=AQH_Device_List_First(deviceList);
|
|
||||||
while(device) {
|
|
||||||
const char *deviceName;
|
|
||||||
|
|
||||||
deviceName=AQH_Device_GetName(device);
|
|
||||||
if (deviceName && *deviceName) {
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", (unsigned long int) AQH_Device_GetId(device));
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
if (selectedDeviceId && AQH_Device_GetId(device)==selectedDeviceId)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\" selected>%s</option>\n", numbuf, deviceName);
|
|
||||||
else
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\">%s</option>\n", numbuf, deviceName);
|
|
||||||
}
|
|
||||||
device=AQH_Device_List_Next(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</select></td></tr>\n");
|
|
||||||
|
|
||||||
/* data type */
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td><label for=\"dataType\">%s: </label></td>"
|
|
||||||
"<td><select id=\"dataType\" name=\"dataType\">",
|
|
||||||
I18N("Data Type"));
|
|
||||||
if (dataType==AQH_MqttTopicType_Num)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%d\" selected>%s</option>", AQH_MqttTopicType_Num, I18N("numeric"));
|
|
||||||
else
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%d\" >%s</option>", AQH_MqttTopicType_Num, I18N("numeric"));
|
|
||||||
if (dataType==AQH_MqttTopicType_Json)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%d\" selected>%s</option>", AQH_MqttTopicType_Json, I18N("JSON"));
|
|
||||||
else
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%d\" >%s</option>", AQH_MqttTopicType_Json, I18N("JSON"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</select></td></tr>\n");
|
|
||||||
|
|
||||||
|
|
||||||
if (seenTopicsList && GWEN_StringList_Count(seenTopicsList)) {
|
|
||||||
GWEN_STRINGLISTENTRY *se;
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<tr><td valign=\"top\"><label for=\"receivedTopicsList\">%s: </label></td>", I18N("Received Topics"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "<td><output name=\"receivedTopicsList\" id=\"receivedTopicsList\">");
|
|
||||||
|
|
||||||
se=GWEN_StringList_FirstEntry(seenTopicsList);
|
|
||||||
while(se) {
|
|
||||||
const char *s=GWEN_StringListEntry_Data(se);
|
|
||||||
if (s && *s)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "%s<br>\n", s);
|
|
||||||
se=GWEN_StringListEntry_Next(se);
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</output></td></tr>");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</tbody></table>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_MQTT_TOPIC_LIST *rl;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2>"
|
|
||||||
"<table class=\"dataTable\">"
|
|
||||||
"<thead>"
|
|
||||||
" <tr><th>%s</th><th>%s</th><th>%s</th><th></th></tr>"
|
|
||||||
"</thead>",
|
|
||||||
I18N("Mqtt Topics"),
|
|
||||||
I18N("Topic"),
|
|
||||||
I18N("Device"),
|
|
||||||
I18N("Datatype"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "<tbody>");
|
|
||||||
rl=AQH_Storage_GetMqttTopicList(sto);
|
|
||||||
if (rl) {
|
|
||||||
const AQH_MQTT_TOPIC *mqttTopic;
|
|
||||||
|
|
||||||
mqttTopic=AQH_MqttTopic_List_First(rl);
|
|
||||||
while(mqttTopic) {
|
|
||||||
long unsigned int id;
|
|
||||||
int deviceId;
|
|
||||||
const char *topic;
|
|
||||||
int dataType;
|
|
||||||
const char *deviceName=NULL;
|
|
||||||
const AQH_DEVICE *device=NULL;
|
|
||||||
|
|
||||||
id=(long unsigned int) AQH_MqttTopic_GetId(mqttTopic);
|
|
||||||
deviceId=(long unsigned int) AQH_MqttTopic_GetDeviceId(mqttTopic);
|
|
||||||
if (deviceId>0)
|
|
||||||
device=AQH_Storage_GetDeviceById(sto, deviceId);
|
|
||||||
if (device)
|
|
||||||
deviceName=AQH_Device_GetName(device);
|
|
||||||
|
|
||||||
topic=AQH_MqttTopic_GetTopic(mqttTopic);
|
|
||||||
dataType=AQH_MqttTopic_GetDataType(mqttTopic);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td>%s</td><td>%s</td><td>%s</td>"
|
|
||||||
"<td><a href=\"/mqtttopics/edit/%lu\">"
|
|
||||||
"<IMG src=\"/pics/edit.png\" width=32 height=32 align=left border=0></a></td>"
|
|
||||||
"</tr>",
|
|
||||||
topic?topic:"",
|
|
||||||
deviceName?deviceName:"",
|
|
||||||
AQH_MqttTopicType_toString(dataType),
|
|
||||||
id);
|
|
||||||
mqttTopic=AQH_MqttTopic_List_Next(mqttTopic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</tbody>");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "</table><a href=\"/mqtttopics/add\">%s</a><br>", I18N("Add MqttTopic"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_MQTTTOPICS_H
|
|
||||||
#define AQHOME_STORAGE_U_MQTTTOPICS_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_MqttTopicsHttpUrlHandler_new(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,458 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_objects_p.h"
|
|
||||||
#include "./u_base.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_INHERIT(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
|
||||||
static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
|
|
||||||
static void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
static void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf);
|
|
||||||
static GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id);
|
|
||||||
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static GWEN_MSG *_addOrEditAndCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb);
|
|
||||||
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_ObjectsHttpUrlHandler_new(AQH_SERVICE *sv,
|
|
||||||
uint32_t neededPermsList,
|
|
||||||
uint32_t neededPermsAdd,
|
|
||||||
uint32_t neededPermsDel,
|
|
||||||
uint32_t neededPermsEdit,
|
|
||||||
const char *urlForObjectList)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh;
|
|
||||||
|
|
||||||
uh=AQH_HttpUrlHandler_new(sv);
|
|
||||||
GWEN_NEW_OBJECT(AQH_URLHANDLER_OBJECTS, xuh);
|
|
||||||
GWEN_INHERIT_SETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh, xuh, _freeData);
|
|
||||||
|
|
||||||
AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl);
|
|
||||||
|
|
||||||
xuh->neededPermsList=neededPermsList;
|
|
||||||
xuh->neededPermsAdd=neededPermsAdd;
|
|
||||||
xuh->neededPermsDel=neededPermsDel;
|
|
||||||
xuh->neededPermsEdit=neededPermsEdit;
|
|
||||||
xuh->urlForObjectList=urlForObjectList?strdup(urlForObjectList):NULL;
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _freeData(void *bp, void *p)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh;
|
|
||||||
|
|
||||||
xuh=(AQH_URLHANDLER_OBJECTS*)p;
|
|
||||||
free(xuh->urlForObjectList);
|
|
||||||
GWEN_FREE_OBJECT(xuh);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_ADDOREDITOBJECT_FN fn)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh) {
|
|
||||||
xuh->addOrEditObjectFn=fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_FINDOBJECTBYIDANDRETURNASDB_FN fn)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh) {
|
|
||||||
xuh->findObjectByIdAndReturnAsDbFn=fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEADDPAGE_FN fn)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh) {
|
|
||||||
xuh->writeAddPageFn=fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEEDITPAGE_FN fn)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh) {
|
|
||||||
xuh->writeEditPageFn=fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_LISTOBJECTSINTOBUFFER_FN fn)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh) {
|
|
||||||
xuh->listObjectsIntoBufferFn=fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
if (GWEN_INHERIT_ISOFTYPE(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh)) {
|
|
||||||
const char *protocol;
|
|
||||||
const char *cmd;
|
|
||||||
GWEN_MSG *msgOut;
|
|
||||||
|
|
||||||
AQH_HttpService_SetupModuleAndPerms(AQH_HttpUrlHandler_GetHttpService(uh), rq, "aqhome");
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
|
|
||||||
AQH_HttpRequest_SetupUrlPathMembers(rq);
|
|
||||||
if (AQH_HttpRequest_GetUrlPathMembers(rq)==NULL) {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd=AQH_HttpRequest_GetCommand(rq);
|
|
||||||
if (cmd && *cmd) {
|
|
||||||
if (strcasecmp(cmd, "GET")==0)
|
|
||||||
msgOut=_handleGet(uh, rq);
|
|
||||||
else if (strcasecmp(cmd, "POST")==0)
|
|
||||||
msgOut=_handlePost(uh, rq);
|
|
||||||
else {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL);
|
|
||||||
}
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "No command in request");
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Not an AQH_URLHANDLER_OBJECTS object");
|
|
||||||
return GWEN_ERROR_INTERNAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
GWEN_BUFFER *pageBuf;
|
|
||||||
int rv;
|
|
||||||
GWEN_MSG *msgOut=NULL;
|
|
||||||
const char *protocol;
|
|
||||||
const GWEN_STRINGLIST *sl;
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
pageBuf=GWEN_Buffer_new(0, 2048, 0, 1);
|
|
||||||
/* header */
|
|
||||||
rv=AQH_HttpUrlHandler_AddContentHeaders(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers");
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* middle part (header - middle - footer) */
|
|
||||||
sl=AQH_HttpRequest_GetUrlPathMembers(rq);
|
|
||||||
|
|
||||||
s=GWEN_StringList_StringAt(sl, 1);
|
|
||||||
if (!(s && *s))
|
|
||||||
s="list";
|
|
||||||
|
|
||||||
if (strcasecmp(s, "list")==0)
|
|
||||||
_handleGetList(uh, rq, pageBuf);
|
|
||||||
else if (strcasecmp(s, "add")==0)
|
|
||||||
_handleGetAdd(uh, rq, pageBuf);
|
|
||||||
else if (strcasecmp(s, "edit")==0)
|
|
||||||
_handleGetEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0), pageBuf);
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* footer */
|
|
||||||
rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers");
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf));
|
|
||||||
GWEN_Buffer_free(pageBuf);
|
|
||||||
return msgOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
const GWEN_STRINGLIST *sl;
|
|
||||||
const char *protocol;
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "POST:");
|
|
||||||
db=AQH_HttpRequest_GetDbPostBody(rq);
|
|
||||||
GWEN_DB_Dump(db, 2);
|
|
||||||
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
sl=AQH_HttpRequest_GetUrlPathMembers(rq);
|
|
||||||
|
|
||||||
s=GWEN_StringList_StringAt(sl, 1);
|
|
||||||
if (s && *s) {
|
|
||||||
if (strcasecmp(s, "add")==0)
|
|
||||||
return _handlePostAdd(uh, rq);
|
|
||||||
else if (strcasecmp(s, "edit")==0)
|
|
||||||
return _handlePostEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0));
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Invalid url (2nd member missing)");
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
uint32_t perms;
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "LIST");
|
|
||||||
perms=AQH_HttpRequest_GetModulePerms(rq);
|
|
||||||
if (perms & xuh->neededPermsList) {
|
|
||||||
_listObjectsIntoBuffer(uh, pageBuf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<p>%s</p>", I18N("No permissions to see list of objects."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
uint32_t perms;
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "ADD");
|
|
||||||
perms=AQH_HttpRequest_GetModulePerms(rq);
|
|
||||||
if (perms & xuh->neededPermsAdd) {
|
|
||||||
_writeAddPage(uh, rq, NULL, pageBuf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No permissions to add.");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("No permissions to add an object."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
uint32_t perms;
|
|
||||||
|
|
||||||
DBG_ERROR(NULL, "EDIT");
|
|
||||||
perms=AQH_HttpRequest_GetModulePerms(rq);
|
|
||||||
if (perms & xuh->neededPermsEdit) {
|
|
||||||
if (id>0) {
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
db=_findObjectByIdAndReturnAsDb(uh, id);
|
|
||||||
if (db) {
|
|
||||||
_writeEditPage(uh, rq, db, pageBuf);
|
|
||||||
GWEN_DB_Group_free(db);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Object %d not found", id);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("Object not found."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "Missing object id");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("Missing or invalid object id."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No permissions to edit.");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<p>%s</p>", I18N("No permissions to edit."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
uint32_t perms;
|
|
||||||
|
|
||||||
perms=AQH_HttpRequest_GetModulePerms(rq);
|
|
||||||
if (perms & xuh->neededPermsAdd)
|
|
||||||
return _addOrEditAndCreateResponse(uh, rq, 0, _writeAddPage);
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No perms to add object");
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 403, "Forbidden",
|
|
||||||
AQH_HttpRequest_GetProtocol(rq), NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
uint32_t perms;
|
|
||||||
|
|
||||||
perms=AQH_HttpRequest_GetModulePerms(rq);
|
|
||||||
if (perms & xuh->neededPermsEdit) {
|
|
||||||
return _addOrEditAndCreateResponse(uh, rq, id, _writeEditPage);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No perms to edit");
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 403, "Forbidden",
|
|
||||||
AQH_HttpRequest_GetProtocol(rq), NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_addOrEditAndCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
db=AQH_HttpRequest_GetDbPostBody(rq);
|
|
||||||
rv=_addOrEditObject(uh, db, id);
|
|
||||||
if (rv<0)
|
|
||||||
return AQH_BaseHttpUrlHandler_CreateResponseForErrorCode(uh, rq, rv, cb, db);
|
|
||||||
return AQH_HttpService_CreateRedirectingResponseMsg(sv, AQH_HttpRequest_GetProtocol(rq), xuh->urlForObjectList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh->addOrEditObjectFn)
|
|
||||||
return xuh->addOrEditObjectFn(uh, db, id);
|
|
||||||
return GWEN_ERROR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh->findObjectByIdAndReturnAsDbFn)
|
|
||||||
return xuh->findObjectByIdAndReturnAsDbFn(uh, id);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh->writeAddPageFn)
|
|
||||||
return xuh->writeAddPageFn(uh, rq, dbValues, pageBuf);
|
|
||||||
return GWEN_ERROR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh->writeEditPageFn)
|
|
||||||
return xuh->writeEditPageFn(uh, rq, dbValues, pageBuf);
|
|
||||||
return GWEN_ERROR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
|
|
||||||
|
|
||||||
if (xuh->listObjectsIntoBufferFn)
|
|
||||||
xuh->listObjectsIntoBufferFn(uh, pageBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_OBJECTS_H
|
|
||||||
#define AQHOME_STORAGE_U_OBJECTS_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_ObjectsHttpUrlHandler_new(AQH_SERVICE *sv,
|
|
||||||
uint32_t neededPermsList,
|
|
||||||
uint32_t neededPermsAdd,
|
|
||||||
uint32_t neededPermsDel,
|
|
||||||
uint32_t neededPermsEdit,
|
|
||||||
const char *urlForObjectList);
|
|
||||||
|
|
||||||
|
|
||||||
typedef int (*AQH_OBJECTSHTTPURLHANDLER_ADDOREDITOBJECT_FN)(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
typedef GWEN_DB_NODE* (*AQH_OBJECTSHTTPURLHANDLER_FINDOBJECTBYIDANDRETURNASDB_FN)(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
typedef int (*AQH_OBJECTSHTTPURLHANDLER_WRITEADDPAGE_FN)(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq,
|
|
||||||
GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
typedef int (*AQH_OBJECTSHTTPURLHANDLER_WRITEEDITPAGE_FN)(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq,
|
|
||||||
GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
typedef void (*AQH_OBJECTSHTTPURLHANDLER_LISTOBJECTSINTOBUFFER_FN)(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_ADDOREDITOBJECT_FN fn);
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_FINDOBJECTBYIDANDRETURNASDB_FN fn);
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEADDPAGE_FN fn);
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEEDITPAGE_FN fn);
|
|
||||||
void AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_LISTOBJECTSINTOBUFFER_FN fn);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_OBJECTS_P_H
|
|
||||||
#define AQHOME_STORAGE_U_OBJECTS_P_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_objects.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/stringlist.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct AQH_URLHANDLER_OBJECTS AQH_URLHANDLER_OBJECTS;
|
|
||||||
struct AQH_URLHANDLER_OBJECTS {
|
|
||||||
uint32_t neededPermsList;
|
|
||||||
uint32_t neededPermsAdd;
|
|
||||||
uint32_t neededPermsDel;
|
|
||||||
uint32_t neededPermsEdit; /* e.g. AQHOME_HTTP_PERMS_EDIT_ROOM */
|
|
||||||
|
|
||||||
char *urlForObjectList;
|
|
||||||
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_ADDOREDITOBJECT_FN addOrEditObjectFn;
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_FINDOBJECTBYIDANDRETURNASDB_FN findObjectByIdAndReturnAsDbFn;
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_WRITEADDPAGE_FN writeAddPageFn;
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_WRITEEDITPAGE_FN writeEditPageFn;
|
|
||||||
AQH_OBJECTSHTTPURLHANDLER_LISTOBJECTSINTOBUFFER_FN listObjectsIntoBufferFn;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_rooms.h"
|
|
||||||
#include "./u_objects.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
static void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _setFromObject(AQH_ROOM *r, const AQH_ROOM *srcRoom);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_RoomsHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_ObjectsHttpUrlHandler_new(sv,
|
|
||||||
AQHOME_HTTP_PERMS_LIST_ROOMS,
|
|
||||||
AQHOME_HTTP_PERMS_ADD_ROOM,
|
|
||||||
AQHOME_HTTP_PERMS_DEL_ROOM,
|
|
||||||
AQHOME_HTTP_PERMS_EDIT_ROOM,
|
|
||||||
"/rooms/list");
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(uh, _addOrEditObject);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(uh, _findObjectByIdAndReturnAsDb);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(uh, _writeAddPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(uh, _writeEditPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(uh, _listObjectsIntoBuffer);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
AQH_ROOM *newRoom;
|
|
||||||
const char *roomName;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
newRoom=AQH_Room_fromDb(db);
|
|
||||||
|
|
||||||
roomName=AQH_Room_GetName(newRoom);
|
|
||||||
if (!(roomName && *roomName)) {
|
|
||||||
DBG_INFO(NULL, "Missing room name");
|
|
||||||
AQH_Room_free(newRoom);
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_LockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error locking storage");
|
|
||||||
AQH_Room_free(newRoom);
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id>0) {
|
|
||||||
AQH_ROOM *r;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Edit existing room");
|
|
||||||
r=AQH_Storage_GetRoomById(sto, id);
|
|
||||||
if (r==NULL) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Room %d not found", id);
|
|
||||||
AQH_Room_free(newRoom);
|
|
||||||
return GWEN_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
AQH_Room_SetId(r, id);
|
|
||||||
_setFromObject(r, newRoom);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AQH_ROOM *r;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Adding new room");
|
|
||||||
r=AQH_Storage_GetRoomByName(sto, roomName);
|
|
||||||
if (r) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Room %s exists", roomName);
|
|
||||||
AQH_Room_free(newRoom);
|
|
||||||
return GWEN_ERROR_FOUND;
|
|
||||||
}
|
|
||||||
r=AQH_Room_new();
|
|
||||||
AQH_Room_SetId(r, 0);
|
|
||||||
_setFromObject(r, newRoom);
|
|
||||||
AQH_Storage_AddRoom(sto, r);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
AQH_Room_free(newRoom);
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error unlocking storage");
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setFromObject(AQH_ROOM *r, const AQH_ROOM *srcRoom)
|
|
||||||
{
|
|
||||||
AQH_Room_SetName(r, AQH_Room_GetName(srcRoom));
|
|
||||||
AQH_Room_SetDescription(r, AQH_Room_GetDescription(srcRoom));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_ROOM *r;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
r=AQH_Storage_GetRoomById(sto, id);
|
|
||||||
if (r) {
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
db=GWEN_DB_Group_new("room");
|
|
||||||
AQH_Room_toDb(r, db);
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/rooms/add\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Add Room"));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/rooms/edit/%lu\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Edit Room"),
|
|
||||||
(long unsigned int)(dbValues?GWEN_DB_GetIntValue(dbValues, "id", 0, 0):0));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <table>"
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"name\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"name\" value=\"%s\" required></td>"
|
|
||||||
" </tr>"
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"description\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"description\" value=\"%s\" ></td>"
|
|
||||||
" </tr>"
|
|
||||||
" </table>",
|
|
||||||
I18N("Name"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "name", 0, ""):"",
|
|
||||||
I18N("Description"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "description", 0, ""):"");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_ROOM_LIST *rl;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2>"
|
|
||||||
"<table class=\"dataTable\">"
|
|
||||||
"<thead>"
|
|
||||||
" <tr><th>%s</th><th>%s</th><th></th></tr>"
|
|
||||||
"</thead>",
|
|
||||||
I18N("Rooms"),
|
|
||||||
I18N("Name"),
|
|
||||||
I18N("Description"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "<tbody>");
|
|
||||||
rl=AQH_Storage_GetRoomList(sto);
|
|
||||||
if (rl) {
|
|
||||||
const AQH_ROOM *r;
|
|
||||||
|
|
||||||
r=AQH_Room_List_First(rl);
|
|
||||||
while(r) {
|
|
||||||
long unsigned int id;
|
|
||||||
const char *name;
|
|
||||||
const char *descr;
|
|
||||||
|
|
||||||
id=(long unsigned int) AQH_Room_GetId(r);
|
|
||||||
name=AQH_Room_GetName(r);
|
|
||||||
descr=AQH_Room_GetDescription(r);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td>%s</td><td>%s</td>"
|
|
||||||
"<td><a href=\"/rooms/edit/%lu\">"
|
|
||||||
"<IMG src=\"/pics/edit.png\" width=32 height=32 align=left border=0></a></td>"
|
|
||||||
"</tr>",
|
|
||||||
name?name:"", descr?descr:"", id);
|
|
||||||
r=AQH_Room_List_Next(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</tbody>");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "</table><a href=\"/rooms/add\">%s</a><br>", I18N("Add Room"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_ROOMS_H
|
|
||||||
#define AQHOME_STORAGE_U_ROOMS_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_RoomsHttpUrlHandler_new(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_static_p.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
#include "aqhome/http/httpservice.h"
|
|
||||||
#include "aqhome/http/httpservice_http.h"
|
|
||||||
#include "aqhome/http/httpservice_conf.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_INHERIT(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
|
|
||||||
static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
|
|
||||||
static void _readFileList(AQH_HTTP_URLHANDLER *uh);
|
|
||||||
static GWEN_BUFFER *_readFileIntoBuffer(AQH_HTTP_URLHANDLER *uh, const char *requestedFilename);
|
|
||||||
static const char *_getContentTypeFromFilename(const char *filename);
|
|
||||||
static GWEN_MSG *_createFileResponseMsg(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
const char *protocol,
|
|
||||||
const char *contentType,
|
|
||||||
const uint8_t *ptr, uint32_t len);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_StaticHttpUrlHandler_new(AQH_SERVICE *sv, const char *folder)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
AQH_URLHANDLER_STATIC *xuh;
|
|
||||||
|
|
||||||
uh=AQH_HttpUrlHandler_new(sv);
|
|
||||||
GWEN_NEW_OBJECT(AQH_URLHANDLER_STATIC, xuh);
|
|
||||||
GWEN_INHERIT_SETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh, xuh, _freeData);
|
|
||||||
|
|
||||||
AQH_HttpUrlHandler_SetFolder(uh, folder);
|
|
||||||
AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl);
|
|
||||||
|
|
||||||
_readFileList(uh);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _freeData(void *bp, void *p)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_STATIC *xuh;
|
|
||||||
|
|
||||||
xuh=(AQH_URLHANDLER_STATIC*)p;
|
|
||||||
GWEN_StringList_free(xuh->fileList);
|
|
||||||
GWEN_FREE_OBJECT(xuh);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
const char *protocol;
|
|
||||||
const char *cmd;
|
|
||||||
GWEN_MSG *msgOut;
|
|
||||||
|
|
||||||
AQH_HttpService_SetupModuleAndPerms(AQH_HttpUrlHandler_GetHttpService(uh), rq, "aqhome");
|
|
||||||
AQH_HttpRequest_SetupUrlPathMembers(rq);
|
|
||||||
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
cmd=AQH_HttpRequest_GetCommand(rq);
|
|
||||||
if (cmd && *cmd) {
|
|
||||||
if (strcasecmp(cmd, "GET")==0)
|
|
||||||
msgOut=_handleGet(uh, rq);
|
|
||||||
else {
|
|
||||||
msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL);
|
|
||||||
}
|
|
||||||
AQH_HttpRequest_SetResponseMsg(rq, msgOut);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "No command in request");
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_STATIC *xuh;
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
const char *protocol;
|
|
||||||
const GWEN_STRINGLIST *sl;
|
|
||||||
|
|
||||||
xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh);
|
|
||||||
if (xuh==NULL) {
|
|
||||||
DBG_ERROR(NULL, "Not a AQH_URLHANDLER_STATIC object");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
protocol=AQH_HttpRequest_GetProtocol(rq);
|
|
||||||
|
|
||||||
/* handle middle part (header - middle - footer) */
|
|
||||||
sl=AQH_HttpRequest_GetUrlPathMembers(rq);
|
|
||||||
if (sl) {
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
s=GWEN_StringList_StringAt(sl, 1);
|
|
||||||
if (!(s && *s)) {
|
|
||||||
DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
|
|
||||||
return AQH_HttpService_CreateResponseMsg(sv, 404, "Not found", protocol, NULL);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GWEN_BUFFER *fileBuf;
|
|
||||||
const char *contentType;
|
|
||||||
GWEN_MSG *msgOut;
|
|
||||||
|
|
||||||
contentType=_getContentTypeFromFilename(s);
|
|
||||||
DBG_INFO(NULL, "Reading file \"%s\" (%s)", s, contentType);
|
|
||||||
fileBuf=_readFileIntoBuffer(uh, s);
|
|
||||||
if (fileBuf==NULL) {
|
|
||||||
DBG_INFO(NULL, "here");
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
|
|
||||||
}
|
|
||||||
msgOut=_createFileResponseMsg(uh, protocol, contentType,
|
|
||||||
(const uint8_t*) GWEN_Buffer_GetStart(fileBuf), GWEN_Buffer_GetUsedBytes(fileBuf));
|
|
||||||
GWEN_Buffer_free(fileBuf);
|
|
||||||
return msgOut;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_ERROR(NULL, "No list of url members");
|
|
||||||
return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _readFileList(AQH_HTTP_URLHANDLER *uh)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_STATIC *xuh;
|
|
||||||
const char *folder;
|
|
||||||
|
|
||||||
xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh);
|
|
||||||
|
|
||||||
GWEN_StringList_free(xuh->fileList);
|
|
||||||
xuh->fileList=NULL;
|
|
||||||
folder=AQH_HttpUrlHandler_GetFolder(uh);
|
|
||||||
if (folder && *folder) {
|
|
||||||
GWEN_STRINGLIST *fileList;
|
|
||||||
|
|
||||||
fileList=AQH_HttpService_GetFolderFileList(folder, NULL, 1);
|
|
||||||
if (fileList) {
|
|
||||||
GWEN_StringList_RemoveString(fileList, ".");
|
|
||||||
GWEN_StringList_RemoveString(fileList, "..");
|
|
||||||
GWEN_StringList_free(xuh->fileList);
|
|
||||||
xuh->fileList=fileList;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "No file list (empty folder?)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_BUFFER *_readFileIntoBuffer(AQH_HTTP_URLHANDLER *uh, const char *requestedFilename)
|
|
||||||
{
|
|
||||||
AQH_URLHANDLER_STATIC *xuh;
|
|
||||||
const char *folder;
|
|
||||||
|
|
||||||
xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_STATIC, uh);
|
|
||||||
folder=AQH_HttpUrlHandler_GetFolder(uh);
|
|
||||||
|
|
||||||
if (!(xuh->fileList && GWEN_StringList_Count(xuh->fileList)>0)) {
|
|
||||||
DBG_INFO(NULL, "Reading file list for folder \"%s\"", folder);
|
|
||||||
_readFileList(uh);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(xuh->fileList && GWEN_StringList_Count(xuh->fileList)>0)) {
|
|
||||||
DBG_INFO(NULL, "No file list (empty or missing/bad folder? Permissions?");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GWEN_StringList_HasString(xuh->fileList, requestedFilename)) {
|
|
||||||
GWEN_BUFFER *nameBuf;
|
|
||||||
GWEN_BUFFER *fileBuf;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
fileBuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
|
||||||
nameBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, folder);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S);
|
|
||||||
GWEN_Buffer_AppendString(nameBuf, requestedFilename);
|
|
||||||
DBG_INFO(NULL, "Reading file \"%s\"", GWEN_Buffer_GetStart(nameBuf));
|
|
||||||
rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(nameBuf), fileBuf);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error reading file \"%s\" (%d)", GWEN_Buffer_GetStart(nameBuf), rv);
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
GWEN_Buffer_free(fileBuf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
GWEN_Buffer_free(nameBuf);
|
|
||||||
return fileBuf;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DBG_INFO(NULL, "File \"%s\" not found in folder \"%s\"", requestedFilename, folder);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_MSG *_createFileResponseMsg(AQH_HTTP_URLHANDLER *uh,
|
|
||||||
const char *protocol,
|
|
||||||
const char *contentType,
|
|
||||||
const uint8_t *ptr, uint32_t len)
|
|
||||||
{
|
|
||||||
GWEN_BUFFER *buf;
|
|
||||||
GWEN_MSG *msg;
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
buf=GWEN_Buffer_new(0, len+256, 0, 1);
|
|
||||||
AQH_HttpService_AddStatusLine(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, buf);
|
|
||||||
|
|
||||||
db=GWEN_DB_Group_new("header");
|
|
||||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Connection", "Keep-Alive");
|
|
||||||
GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Type", contentType);
|
|
||||||
GWEN_DB_SetIntValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS, "Content-Length", len);
|
|
||||||
AQH_HttpService_AddHeader(AQH_HttpUrlHandler_GetHttpService(uh), db, buf);
|
|
||||||
GWEN_DB_Group_free(db);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendBytes(buf, (const char *) ptr, len);
|
|
||||||
|
|
||||||
msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf));
|
|
||||||
GWEN_Buffer_free(buf);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const char *_getContentTypeFromFilename(const char *filename)
|
|
||||||
{
|
|
||||||
if (filename) {
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
s=strrchr(filename, '.');
|
|
||||||
if (s) {
|
|
||||||
if (strcasecmp(s, ".png")==0)
|
|
||||||
return "image/png";
|
|
||||||
else if (strcasecmp(s, ".jpg")==0)
|
|
||||||
return "image/jpg";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "application/x-binary";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_STATIC_H
|
|
||||||
#define AQHOME_STORAGE_U_STATIC_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/endpoint.h>
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_StaticHttpUrlHandler_new(AQH_SERVICE *sv, const char *folder);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_STATIC_P_H
|
|
||||||
#define AQHOME_STORAGE_U_STATIC_P_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_static.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/stringlist.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct AQH_URLHANDLER_STATIC AQH_URLHANDLER_STATIC;
|
|
||||||
struct AQH_URLHANDLER_STATIC {
|
|
||||||
GWEN_STRINGLIST *fileList;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "./u_values.h"
|
|
||||||
#include "./u_objects.h"
|
|
||||||
#include "./aqhomehttp.h"
|
|
||||||
|
|
||||||
#include <gwenhywfar/gwenhywfar.h>
|
|
||||||
#include <gwenhywfar/debug.h>
|
|
||||||
#include <gwenhywfar/i18n.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* defines
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* forward declarations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
|
|
||||||
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
|
|
||||||
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);
|
|
||||||
|
|
||||||
static void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
|
|
||||||
static void _setFromObject(AQH_VALUE *value, const AQH_VALUE *srcValue);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
|
||||||
* implementations
|
|
||||||
* ------------------------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_ValuesHttpUrlHandler_new(AQH_SERVICE *sv)
|
|
||||||
{
|
|
||||||
AQH_HTTP_URLHANDLER *uh;
|
|
||||||
|
|
||||||
uh=AQH_ObjectsHttpUrlHandler_new(sv,
|
|
||||||
AQHOME_HTTP_PERMS_LIST_VALUES,
|
|
||||||
AQHOME_HTTP_PERMS_ADD_VALUE,
|
|
||||||
AQHOME_HTTP_PERMS_DEL_VALUE,
|
|
||||||
AQHOME_HTTP_PERMS_EDIT_VALUE,
|
|
||||||
"/values/list");
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(uh, _addOrEditObject);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(uh, _findObjectByIdAndReturnAsDb);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(uh, _writeAddPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(uh, _writeEditPage);
|
|
||||||
AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(uh, _listObjectsIntoBuffer);
|
|
||||||
|
|
||||||
return uh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
AQH_VALUE *newValue;
|
|
||||||
const char *valueName;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
newValue=AQH_Value_fromDb(db);
|
|
||||||
|
|
||||||
valueName=AQH_Value_GetNameForSystem(newValue);
|
|
||||||
if (!(valueName && *valueName)) {
|
|
||||||
DBG_INFO(NULL, "Missing value name");
|
|
||||||
AQH_Value_free(newValue);
|
|
||||||
return GWEN_ERROR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_LockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error locking storage");
|
|
||||||
AQH_Value_free(newValue);
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id>0) {
|
|
||||||
AQH_VALUE *value;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Edit existing value");
|
|
||||||
value=AQH_Storage_GetValueById(sto, id);
|
|
||||||
if (value==NULL) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Value %d not found", id);
|
|
||||||
AQH_Value_free(newValue);
|
|
||||||
return GWEN_ERROR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
AQH_Value_SetId(value, id);
|
|
||||||
_setFromObject(value, newValue);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AQH_VALUE *value;
|
|
||||||
|
|
||||||
DBG_INFO(NULL, "Adding new value");
|
|
||||||
value=AQH_Storage_GetValueByName(sto, valueName);
|
|
||||||
if (value) {
|
|
||||||
AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
DBG_ERROR(NULL, "Value %s exists", valueName);
|
|
||||||
AQH_Value_free(newValue);
|
|
||||||
return GWEN_ERROR_FOUND;
|
|
||||||
}
|
|
||||||
value=AQH_Value_new();
|
|
||||||
AQH_Value_SetId(value, 0);
|
|
||||||
_setFromObject(value, newValue);
|
|
||||||
AQH_Storage_AddValue(sto, value);
|
|
||||||
AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED);
|
|
||||||
}
|
|
||||||
AQH_Value_free(newValue);
|
|
||||||
|
|
||||||
rv=AqHomeHttpService_UnlockStorage(sv);
|
|
||||||
if (rv<0) {
|
|
||||||
DBG_ERROR(NULL, "Error unlocking storage");
|
|
||||||
return GWEN_ERROR_IO;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _setFromObject(AQH_VALUE *value, const AQH_VALUE *srcValue)
|
|
||||||
{
|
|
||||||
AQH_Value_SetName(value, AQH_Value_GetName(srcValue));
|
|
||||||
AQH_Value_SetTopicId(value, AQH_Value_GetTopicId(srcValue));
|
|
||||||
AQH_Value_SetValueType(value, AQH_Value_GetValueType(srcValue));
|
|
||||||
AQH_Value_SetValueUnits(value, AQH_Value_GetValueUnits(srcValue));
|
|
||||||
AQH_Value_SetDataPath(value, AQH_Value_GetDataPath(srcValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_VALUE *value;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
value=AQH_Storage_GetValueById(sto, id);
|
|
||||||
if (value) {
|
|
||||||
GWEN_DB_NODE *db;
|
|
||||||
|
|
||||||
db=GWEN_DB_Group_new("value");
|
|
||||||
AQH_Value_toDb(value, db);
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/values/add\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Add Value"));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2><br>\n"
|
|
||||||
"<form action=\"/values/edit/%lu\" method=\"post\" enctype=\"application/x-www-form-urlencoded\">",
|
|
||||||
I18N("Edit Value"),
|
|
||||||
(long unsigned int)(dbValues?GWEN_DB_GetIntValue(dbValues, "id", 0, 0):0));
|
|
||||||
_writeEditingTable(uh, dbValues, pageBuf);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<input type=\"submit\" value=\"%s\"></form>", I18N("Submit"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _writeEditingTable(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_MQTT_TOPIC_LIST *topicList;
|
|
||||||
unsigned long int selectedTopicId=0;
|
|
||||||
char numbuf[16];
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
topicList=AQH_Storage_GetMqttTopicList(sto);
|
|
||||||
|
|
||||||
selectedTopicId=(unsigned long int)(dbValues?GWEN_DB_GetIntValue(dbValues, "TopicId", 0, 0):0);
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", selectedTopicId);
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <table>"
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"name\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"name\" value=\"%s\" required></td>"
|
|
||||||
" </tr>",
|
|
||||||
I18N("Name"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "name", 0, ""):"");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td><label for=\"topicId\">%s: </label></td>"
|
|
||||||
"<td><select id=\"topicId\" name=\"topicId\">",
|
|
||||||
I18N("Topic"));
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"0\">%s</option>", I18N("-- select topic --"));
|
|
||||||
|
|
||||||
if (topicList) {
|
|
||||||
const AQH_MQTT_TOPIC *topic;
|
|
||||||
|
|
||||||
topic=AQH_MqttTopic_List_First(topicList);
|
|
||||||
while(topic) {
|
|
||||||
const char *topicName;
|
|
||||||
|
|
||||||
topicName=AQH_MqttTopic_GetTopic(topic);
|
|
||||||
if (topicName && *topicName) {
|
|
||||||
snprintf(numbuf, sizeof(numbuf)-1, "%lu", (unsigned long int) AQH_MqttTopic_GetId(topic));
|
|
||||||
numbuf[sizeof(numbuf)-1]=0;
|
|
||||||
if (selectedTopicId && AQH_MqttTopic_GetId(topic)==selectedTopicId)
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\" selected>%s</option>", numbuf, topicName);
|
|
||||||
else
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "<option value=\"%s\">%s</option>", numbuf, topicName);
|
|
||||||
}
|
|
||||||
topic=AQH_MqttTopic_List_Next(topic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</select></td></tr>");
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"valueUnits\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"valueUnits\" value=\"%s\"></td>"
|
|
||||||
" </tr>",
|
|
||||||
I18N("Units"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "valueUnits", 0, ""):"");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
" <tr>"
|
|
||||||
" <td><label for=\"dataPath\">%s: </label></td>"
|
|
||||||
" <td><input type=\"text\" name=\"dataPath\" value=\"%s\"></td>"
|
|
||||||
" </tr>"
|
|
||||||
" </table>",
|
|
||||||
I18N("Data Path (JSON)"),
|
|
||||||
dbValues?GWEN_DB_GetCharValue(dbValues, "dataPath", 0, ""):"");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
|
|
||||||
{
|
|
||||||
AQH_SERVICE *sv;
|
|
||||||
AQH_STORAGE *sto;
|
|
||||||
const AQH_VALUE_LIST *rl;
|
|
||||||
|
|
||||||
sv=AQH_HttpUrlHandler_GetHttpService(uh);
|
|
||||||
sto=AqHomeHttpService_GetStorage(sv);
|
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<h2>%s</h2>"
|
|
||||||
"<table class=\"dataTable\">"
|
|
||||||
"<thead>"
|
|
||||||
" <tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th></th></tr>"
|
|
||||||
"</thead>",
|
|
||||||
I18N("Values"),
|
|
||||||
I18N("Name"),
|
|
||||||
I18N("Topic"),
|
|
||||||
I18N("Units"),
|
|
||||||
I18N("Data Path"));
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "<tbody>");
|
|
||||||
rl=AQH_Storage_GetValueList(sto);
|
|
||||||
if (rl) {
|
|
||||||
const AQH_VALUE *value;
|
|
||||||
|
|
||||||
value=AQH_Value_List_First(rl);
|
|
||||||
while(value) {
|
|
||||||
long unsigned int id;
|
|
||||||
int topicId;
|
|
||||||
const char *name;
|
|
||||||
const char *topicName=NULL;
|
|
||||||
const char *valueUnits;
|
|
||||||
const char *dataPath;
|
|
||||||
const AQH_MQTT_TOPIC *topic=NULL;
|
|
||||||
|
|
||||||
id=(long unsigned int) AQH_Value_GetId(value);
|
|
||||||
topicId=(long unsigned int) AQH_Value_GetTopicId(value);
|
|
||||||
if (topicId>0)
|
|
||||||
topic=AQH_Storage_GetMqttTopicById(sto, topicId);
|
|
||||||
if (topic)
|
|
||||||
topicName=AQH_MqttTopic_GetTopic(topic);
|
|
||||||
|
|
||||||
name=AQH_Value_GetName(value);
|
|
||||||
valueUnits=AQH_Value_GetValueUnits(value);
|
|
||||||
dataPath=AQH_Value_GetDataPath(value);
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf,
|
|
||||||
"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
|
|
||||||
"<td><a href=\"/values/edit/%lu\">"
|
|
||||||
"<IMG src=\"/pics/edit.png\" width=32 height=32 align=left border=0></a></td>"
|
|
||||||
"</tr>",
|
|
||||||
name?name:"",
|
|
||||||
topicName?topicName:"",
|
|
||||||
valueUnits?valueUnits:"",
|
|
||||||
dataPath?dataPath:"",
|
|
||||||
id);
|
|
||||||
value=AQH_Value_List_Next(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GWEN_Buffer_AppendString(pageBuf, "</tbody>");
|
|
||||||
GWEN_Buffer_AppendArgs(pageBuf, "</table><a href=\"/values/add\">%s</a><br>", I18N("Add Value"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* This file is part of the project AqHome.
|
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
||||||
*
|
|
||||||
* The license for this file can be found in the file COPYING which you
|
|
||||||
* should have received along with this file.
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_U_VALUES_H
|
|
||||||
#define AQHOME_STORAGE_U_VALUES_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "aqhome/http/urlhandler.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQH_HTTP_URLHANDLER *AQH_ValuesHttpUrlHandler_new(AQH_SERVICE *sv);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
aqhome
|
aqhome
|
||||||
aqhtool_nodes
|
aqhtool_nodes
|
||||||
aqhtool_data
|
aqhtool_data
|
||||||
|
aqhtool_image
|
||||||
</useTargets>
|
</useTargets>
|
||||||
|
|
||||||
<libraries>
|
<libraries>
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
<subdirs>
|
<subdirs>
|
||||||
nodes
|
nodes
|
||||||
data
|
data
|
||||||
|
image
|
||||||
</subdirs>
|
</subdirs>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
#include <gwenhywfar/i18n.h>
|
#include <gwenhywfar/i18n.h>
|
||||||
#include <gwenhywfar/debug.h>
|
#include <gwenhywfar/debug.h>
|
||||||
#include <gwenhywfar/text.h>
|
#include <gwenhywfar/text.h>
|
||||||
|
#include <gwenhywfar/stringlist.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -46,6 +50,10 @@
|
|||||||
|
|
||||||
static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
|
static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
|
||||||
static int _readValueFromString(const char *s, double *pDouble);
|
static int _readValueFromString(const char *s, double *pDouble);
|
||||||
|
static int _readValueFromStringList(const char *s, double *pDouble);
|
||||||
|
static int _read16BitValues(const GWEN_STRINGLIST *sl, double *pDouble);
|
||||||
|
static int _read8BitValues(const GWEN_STRINGLIST *sl, double *pDouble);
|
||||||
|
static int _readDouble(const char *s, double *pDouble);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -130,6 +138,115 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
|
|||||||
|
|
||||||
|
|
||||||
int _readValueFromString(const char *s, double *pDouble)
|
int _readValueFromString(const char *s, double *pDouble)
|
||||||
|
{
|
||||||
|
if (s && *s) {
|
||||||
|
if (strchr(s, ':')!=NULL)
|
||||||
|
return _readValueFromStringList(s, pDouble);
|
||||||
|
else
|
||||||
|
return _readDouble(s, pDouble);
|
||||||
|
}
|
||||||
|
DBG_ERROR(NULL, "Missing value");
|
||||||
|
return GWEN_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _readValueFromStringList(const char *s, double *pDouble)
|
||||||
|
{
|
||||||
|
GWEN_STRINGLIST *sl;
|
||||||
|
|
||||||
|
sl=GWEN_StringList_fromString2(s, ":", 0, GWEN_TEXT_FLAGS_DEL_QUOTES);
|
||||||
|
if (sl) {
|
||||||
|
int cnt;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
cnt=GWEN_StringList_Count(sl);
|
||||||
|
if (cnt<3){ /* read two 16 bit values */
|
||||||
|
rv=_read16BitValues(sl, pDouble);
|
||||||
|
GWEN_StringList_free(sl);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
else if (cnt<5) { /* read four 8 bit values */
|
||||||
|
rv=_read8BitValues(sl, pDouble);
|
||||||
|
GWEN_StringList_free(sl);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Bad value \"%s\"", s);
|
||||||
|
return GWEN_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Bad value \"%s\"", s);
|
||||||
|
return GWEN_ERROR_GENERIC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _read16BitValues(const GWEN_STRINGLIST *sl, double *pDouble)
|
||||||
|
{
|
||||||
|
const GWEN_STRINGLISTENTRY *se;
|
||||||
|
uint32_t result=0;
|
||||||
|
|
||||||
|
se=GWEN_StringList_FirstEntry(sl);
|
||||||
|
while(se) {
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
s=GWEN_StringListEntry_Data(se);
|
||||||
|
if (s && *s) {
|
||||||
|
double v;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv=_readDouble(s, &v);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "here (%d)", rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
result<<=16;
|
||||||
|
result+=(((uint32_t) v) & 0xffff);
|
||||||
|
}
|
||||||
|
se=GWEN_StringListEntry_Next(se);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pDouble=(double) result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _read8BitValues(const GWEN_STRINGLIST *sl, double *pDouble)
|
||||||
|
{
|
||||||
|
const GWEN_STRINGLISTENTRY *se;
|
||||||
|
uint32_t result=0;
|
||||||
|
|
||||||
|
se=GWEN_StringList_FirstEntry(sl);
|
||||||
|
while(se) {
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
s=GWEN_StringListEntry_Data(se);
|
||||||
|
if (s && *s) {
|
||||||
|
double v;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv=_readDouble(s, &v);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "here (%d)", rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
result<<=8;
|
||||||
|
result+=(((uint32_t) v) & 0xff);
|
||||||
|
}
|
||||||
|
se=GWEN_StringListEntry_Next(se);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pDouble=(double) result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _readDouble(const char *s, double *pDouble)
|
||||||
{
|
{
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
@@ -144,9 +261,9 @@ int _readValueFromString(const char *s, double *pDouble)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (l>1 && s[0]=='0' && ((tolower(s[1])=='x') || tolower(s[1])=='b')) {
|
else if (l>1 && s[0]=='0' && ((tolower(s[1])=='x') || tolower(s[1])=='b')) {
|
||||||
unsigned int h;
|
int h;
|
||||||
|
|
||||||
if (1==sscanf(s, "%u", &h)) {
|
if (1==sscanf(s, "%i", &h)) {
|
||||||
*pDouble=(double) h;
|
*pDouble=(double) h;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -155,14 +272,21 @@ int _readValueFromString(const char *s, double *pDouble)
|
|||||||
double d;
|
double d;
|
||||||
|
|
||||||
if (1==sscanf(s, "%lf", &d)) {
|
if (1==sscanf(s, "%lf", &d)) {
|
||||||
return d;
|
*pDouble=d;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBG_ERROR(NULL, "Bad value \"%s\"", s);
|
DBG_ERROR(NULL, "Bad value \"%s\"", s);
|
||||||
return GWEN_ERROR_GENERIC;
|
return GWEN_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_ERROR(NULL, "Empty value");
|
||||||
|
return GWEN_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
62
apps/aqhome-tool/image/0BUILD
Normal file
62
apps/aqhome-tool/image/0BUILD
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml?>
|
||||||
|
|
||||||
|
<gwbuild>
|
||||||
|
|
||||||
|
<target type="ConvenienceLibrary" name="aqhtool_image" >
|
||||||
|
|
||||||
|
<includes type="c" >
|
||||||
|
$(gwenhywfar_cflags)
|
||||||
|
-I$(topsrcdir)
|
||||||
|
-I$(topbuilddir)
|
||||||
|
</includes>
|
||||||
|
|
||||||
|
<includes type="tm2" >
|
||||||
|
--include=$(builddir)
|
||||||
|
--include=$(srcdir)
|
||||||
|
</includes>
|
||||||
|
|
||||||
|
<setVar name="local/cflags">$(visibility_cflags)</setVar>
|
||||||
|
|
||||||
|
<setVar name="tm2flags" >
|
||||||
|
</setVar>
|
||||||
|
|
||||||
|
<setVar name="local/typefiles" >
|
||||||
|
</setVar>
|
||||||
|
|
||||||
|
<setVar name="local/built_sources" >
|
||||||
|
</setVar>
|
||||||
|
|
||||||
|
<setVar name="local/built_headers_pub">
|
||||||
|
</setVar>
|
||||||
|
|
||||||
|
<setVar name="local/built_headers_priv" >
|
||||||
|
</setVar>
|
||||||
|
|
||||||
|
<headers dist="true" >
|
||||||
|
readbmp.h
|
||||||
|
</headers>
|
||||||
|
|
||||||
|
<sources>
|
||||||
|
$(local/typefiles)
|
||||||
|
|
||||||
|
readbmp.c
|
||||||
|
</sources>
|
||||||
|
|
||||||
|
<useTargets>
|
||||||
|
</useTargets>
|
||||||
|
|
||||||
|
<libraries>
|
||||||
|
</libraries>
|
||||||
|
|
||||||
|
<subdirs>
|
||||||
|
</subdirs>
|
||||||
|
|
||||||
|
|
||||||
|
<extradist>
|
||||||
|
</extradist>
|
||||||
|
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
</gwbuild>
|
||||||
652
apps/aqhome-tool/image/readbmp.c
Normal file
652
apps/aqhome-tool/image/readbmp.c
Normal file
@@ -0,0 +1,652 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 "./readbmp.h"
|
||||||
|
|
||||||
|
#include <gwenhywfar/i18n.h>
|
||||||
|
#include <gwenhywfar/debug.h>
|
||||||
|
#include <gwenhywfar/text.h>
|
||||||
|
#include <gwenhywfar/args.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
|
||||||
|
|
||||||
|
|
||||||
|
#define BMP_FILE_OFFS_FILEHEADER 0
|
||||||
|
#define BMP_FILE_OFFS_IMAGEHEADER 14
|
||||||
|
|
||||||
|
#define BMP_FILEHEADER_OFFS_TYPE 0 /* 2 bytes ("BM") */
|
||||||
|
#define BMP_FILEHEADER_OFFS_FSIZE 2 /* 4 bytes */
|
||||||
|
#define BMP_FILEHEADER_OFFS_RESERVED1 6 /* 2 bytes */
|
||||||
|
#define BMP_FILEHEADER_OFFS_RESERVED2 8 /* 2 bytes */
|
||||||
|
#define BMP_FILEHEADER_OFFS_PIXELOFFS 10 /* 4 bytes offset to begin of pixel data */
|
||||||
|
#define BMP_FILEHEADER_SIZE 14
|
||||||
|
|
||||||
|
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_HSIZE 0 /* 4 bytes header size */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_WIDTH 4 /* 4 bytes */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_HEIGHT 8 /* 4 bytes */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_PLANES 12 /* 2 bytes (1) */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_BPP 14 /* 2 bytes bit per pixel */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_COMPR 16 /* 4 bytes (0=uncompressed) */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_IMGSIZE 20 /* 4 bytes (may be 0 for uncompressed data) */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_XPPM 24 /* 4 bytes X pixel per meter */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_YPPM 28 /* 4 bytes Y pixel per meter */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_COLORMAPENTRIES 32 /* 4 bytes number of color map entries actually used */
|
||||||
|
#define BMP_IMAGEHEADER_OFFS_IMPORTANT 36 /* 4 bytes number of significant colors */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* forward declarations
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int AQH_Tool_ReadAndDumpBmpFile(const char *fname);
|
||||||
|
static int AQH_Tool_ExportBmpFile(const char *fname);
|
||||||
|
|
||||||
|
static int _exportBmp_1bpp(const BMP_FILE *bf);
|
||||||
|
static int _exportBmp_gray8bpp(const BMP_FILE *bf);
|
||||||
|
|
||||||
|
static GWEN_BUFFER *_extractPixels_gray8bpp(const uint8_t *ptrPixels, int imageWidth, int imageHeight);
|
||||||
|
static void _printBytes_ASM(const char *sName, const uint8_t *ptrPixels, int lenPixels, int imageWidth, int imageHeight);
|
||||||
|
static GWEN_BUFFER *_rleEncode(const uint8_t *ptrPixels, int lenPixels);
|
||||||
|
static int _countRepeats(const uint8_t *ptrPixels, int lenPixels);
|
||||||
|
|
||||||
|
|
||||||
|
static BMP_FILEHEADER *_fileHeader_new();
|
||||||
|
static void _fileHeader_free(BMP_FILEHEADER *fh);
|
||||||
|
|
||||||
|
static BMP_IMAGEHEADER *_imageHeader_new();
|
||||||
|
static void _imageHeader_free(BMP_IMAGEHEADER *ih);
|
||||||
|
|
||||||
|
static void _dumpBmpFileHeader(const BMP_FILEHEADER *fh);
|
||||||
|
static void _dumpBmpImageHeader(const BMP_IMAGEHEADER *ih);
|
||||||
|
static BMP_FILEHEADER *_readFileHeaderAt(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset);
|
||||||
|
static BMP_IMAGEHEADER *_readImageHeaderAt(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset);
|
||||||
|
|
||||||
|
static uint16_t _readUint16At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, uint16_t defaultValue);
|
||||||
|
static uint32_t _readUint32At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, uint32_t defaultValue);
|
||||||
|
static int32_t _readInt32At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, int32_t defaultValue);
|
||||||
|
|
||||||
|
static GWEN_BUFFER *_readFileIntoBuffer(const char *fname);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* code
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_Tool_DumpBmpFile(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbLocalArgs;
|
||||||
|
int rv;
|
||||||
|
const char *sFilename;
|
||||||
|
const GWEN_ARGS args[]= {
|
||||||
|
/* flags type name min max s long short_descr, long_descr */
|
||||||
|
{ A_ARG, A_CHAR, "bmpFile", 1, 1, "i", "bmpFile", I18S("BMP file to read"), NULL},
|
||||||
|
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local");
|
||||||
|
rv=GWEN_Args_Check(argc, argv, 1, GWEN_ARGS_MODE_ALLOW_FREEPARAM, args, dbLocalArgs);
|
||||||
|
if (rv==GWEN_ARGS_RESULT_ERROR) {
|
||||||
|
fprintf(stderr, "ERROR: Could not parse arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (rv==GWEN_ARGS_RESULT_HELP) {
|
||||||
|
GWEN_BUFFER *ubuf;
|
||||||
|
|
||||||
|
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
|
||||||
|
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
|
||||||
|
fprintf(stderr, "ERROR: Could not create help string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf));
|
||||||
|
GWEN_Buffer_free(ubuf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sFilename=GWEN_DB_GetCharValue(dbLocalArgs, "bmpFile", 0, NULL);
|
||||||
|
if (!(sFilename && *sFilename)) {
|
||||||
|
fprintf(stderr, "Missing file name\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return AQH_Tool_ReadAndDumpBmpFile(sFilename);
|
||||||
|
return AQH_Tool_ExportBmpFile(sFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_Tool_ReadAndDumpBmpFile(const char *fname)
|
||||||
|
{
|
||||||
|
BMP_FILE *bf;
|
||||||
|
|
||||||
|
bf=BMP_File_fromFile(fname);
|
||||||
|
if (bf) {
|
||||||
|
_dumpBmpFileHeader(bf->fileHeader);
|
||||||
|
_dumpBmpImageHeader(bf->imageHeader);
|
||||||
|
BMP_File_free(bf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_Tool_ExportBmpFile(const char *fname)
|
||||||
|
{
|
||||||
|
BMP_FILE *bf;
|
||||||
|
|
||||||
|
bf=BMP_File_fromFile(fname);
|
||||||
|
if (bf) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
_dumpBmpFileHeader(bf->fileHeader);
|
||||||
|
_dumpBmpImageHeader(bf->imageHeader);
|
||||||
|
if (bf->imageHeader->bitsPerPixel==1)
|
||||||
|
rv=_exportBmp_1bpp(bf);
|
||||||
|
else if (bf->imageHeader->bitsPerPixel==8)
|
||||||
|
rv=_exportBmp_gray8bpp(bf);
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Invalid bits per pixel (%d)", bf->imageHeader->bitsPerPixel);
|
||||||
|
rv=2;
|
||||||
|
}
|
||||||
|
|
||||||
|
BMP_File_free(bf);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
return 2;;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _exportBmp_1bpp(const BMP_FILE *bf)
|
||||||
|
{
|
||||||
|
const uint8_t *ptrBuffer;
|
||||||
|
uint32_t offsPixels;
|
||||||
|
const uint8_t *ptrPixels;
|
||||||
|
int imageWidth;
|
||||||
|
int imageHeight;
|
||||||
|
int rowWidthInBytes;
|
||||||
|
int columns;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
ptrBuffer=(const uint8_t *) GWEN_Buffer_GetStart(bf->buffer);
|
||||||
|
offsPixels=bf->fileHeader->offsPixels;
|
||||||
|
ptrPixels=ptrBuffer+offsPixels;
|
||||||
|
imageWidth=bf->imageHeader->imgWidth;
|
||||||
|
imageHeight=bf->imageHeader->imgHeight;
|
||||||
|
columns=imageWidth/8;
|
||||||
|
rowWidthInBytes=4*((columns+3)/4); /* BMPs have multiple of 4 bytes per row! */
|
||||||
|
fprintf(stdout, "imgData: \n");
|
||||||
|
fprintf(stdout, " .dw %d, %d\n", imageWidth, imageHeight);
|
||||||
|
|
||||||
|
for (y=imageHeight-1; y>=0; y--) {
|
||||||
|
const uint8_t *rowPtr;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
fprintf(stdout, " .db ");
|
||||||
|
rowPtr=ptrPixels+(y*rowWidthInBytes);
|
||||||
|
for (x=0; x<columns; x++) {
|
||||||
|
if (x)
|
||||||
|
fprintf(stdout, ", ");
|
||||||
|
fprintf(stdout, "0x%02x", rowPtr[x]^0xff);
|
||||||
|
}
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _exportBmp_gray8bpp(const BMP_FILE *bf)
|
||||||
|
{
|
||||||
|
const uint8_t *ptrBuffer;
|
||||||
|
uint32_t offsPixels;
|
||||||
|
const uint8_t *ptrPixels;
|
||||||
|
int imageWidth;
|
||||||
|
int imageHeight;
|
||||||
|
GWEN_BUFFER *pixelBuf;
|
||||||
|
GWEN_BUFFER *asmBuf;
|
||||||
|
|
||||||
|
ptrBuffer=(const uint8_t *) GWEN_Buffer_GetStart(bf->buffer);
|
||||||
|
offsPixels=bf->fileHeader->offsPixels;
|
||||||
|
ptrPixels=ptrBuffer+offsPixels;
|
||||||
|
imageWidth=bf->imageHeader->imgWidth;
|
||||||
|
imageHeight=bf->imageHeader->imgHeight;
|
||||||
|
|
||||||
|
pixelBuf=_extractPixels_gray8bpp(ptrPixels, imageWidth, imageHeight);
|
||||||
|
asmBuf=_rleEncode((const uint8_t*) GWEN_Buffer_GetStart(pixelBuf), GWEN_Buffer_GetUsedBytes(pixelBuf));
|
||||||
|
_printBytes_ASM("imageData",
|
||||||
|
(const uint8_t*) GWEN_Buffer_GetStart(asmBuf), GWEN_Buffer_GetUsedBytes(asmBuf),
|
||||||
|
imageWidth, imageHeight);
|
||||||
|
|
||||||
|
fprintf(stderr, "Compression: %d -> %d bytes\n", GWEN_Buffer_GetUsedBytes(pixelBuf), GWEN_Buffer_GetUsedBytes(asmBuf));
|
||||||
|
GWEN_Buffer_free(asmBuf);
|
||||||
|
GWEN_Buffer_free(pixelBuf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_BUFFER *_extractPixels_gray8bpp(const uint8_t *ptrPixels, int imageWidth, int imageHeight)
|
||||||
|
{
|
||||||
|
int rowWidthInBytes;
|
||||||
|
int columns;
|
||||||
|
int y;
|
||||||
|
GWEN_BUFFER *destBuf;
|
||||||
|
|
||||||
|
destBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
columns=imageWidth;
|
||||||
|
rowWidthInBytes=4*((columns+3)/4); /* BMPs have multiple of 4 bytes per row! */
|
||||||
|
|
||||||
|
for (y=imageHeight-1; y>=0; y--) {
|
||||||
|
const uint8_t *rowPtr;
|
||||||
|
int x;
|
||||||
|
uint8_t currentByte=0;
|
||||||
|
int packedPixels=0;
|
||||||
|
|
||||||
|
rowPtr=ptrPixels+(y*rowWidthInBytes);
|
||||||
|
for (x=0; x<columns; x++) {
|
||||||
|
uint8_t pixel;
|
||||||
|
uint8_t newPix;
|
||||||
|
|
||||||
|
pixel=rowPtr[x];
|
||||||
|
switch(pixel) {
|
||||||
|
case 0x00: newPix=0b01; break; /* outline color */
|
||||||
|
case 0x60: newPix=0b10; break; /* color 1 */
|
||||||
|
case 0xc0: newPix=0b11; break; /* color 2 */
|
||||||
|
case 0xff: newPix=0b00; break; /* background color */
|
||||||
|
default: newPix=0b00; break; /* background color */
|
||||||
|
}
|
||||||
|
currentByte<<=2;
|
||||||
|
currentByte|=newPix;
|
||||||
|
packedPixels++;
|
||||||
|
if (packedPixels==4) {
|
||||||
|
GWEN_Buffer_AppendByte(destBuf, currentByte);
|
||||||
|
packedPixels=0;
|
||||||
|
currentByte=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GWEN_Buffer_GetUsedBytes(destBuf)==0) {
|
||||||
|
GWEN_Buffer_free(destBuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return destBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _printBytes_ASM(const char *sName, const uint8_t *ptrPixels, int lenPixels, int imageWidth, int imageHeight)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stdout, "%s: \n", sName);
|
||||||
|
fprintf(stdout, " .dw %d, %d ; width, height\n", imageWidth, imageHeight);
|
||||||
|
for (i=0; i<lenPixels; i++) {
|
||||||
|
uint8_t currentByte;
|
||||||
|
|
||||||
|
currentByte=ptrPixels[i];
|
||||||
|
if ((i & 15)==0) {
|
||||||
|
if (i)
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
fprintf(stdout, " .db 0x%02x", currentByte);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stdout, ", 0x%02x", currentByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_BUFFER *_rleEncode(const uint8_t *ptrPixels, int lenPixels)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *resultBuf;
|
||||||
|
GWEN_BUFFER *currentBuf;
|
||||||
|
int numCurrentBuf;
|
||||||
|
|
||||||
|
resultBuf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||||
|
currentBuf=GWEN_Buffer_new(0, 128, 0, 1);
|
||||||
|
|
||||||
|
while(ptrPixels && lenPixels>0) {
|
||||||
|
int countRepeats;
|
||||||
|
|
||||||
|
countRepeats=_countRepeats(ptrPixels, lenPixels);
|
||||||
|
if (countRepeats<4) {
|
||||||
|
GWEN_Buffer_AppendByte(currentBuf, *ptrPixels);
|
||||||
|
numCurrentBuf=GWEN_Buffer_GetUsedBytes(currentBuf);
|
||||||
|
if (numCurrentBuf==127) {
|
||||||
|
GWEN_Buffer_AppendByte(resultBuf, numCurrentBuf); /* bit 7 =0 */
|
||||||
|
GWEN_Buffer_AppendBytes(resultBuf, GWEN_Buffer_GetStart(currentBuf), numCurrentBuf);
|
||||||
|
GWEN_Buffer_Reset(currentBuf);
|
||||||
|
}
|
||||||
|
ptrPixels++;
|
||||||
|
lenPixels--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
numCurrentBuf=GWEN_Buffer_GetUsedBytes(currentBuf);
|
||||||
|
if (numCurrentBuf>0) {
|
||||||
|
GWEN_Buffer_AppendByte(resultBuf, numCurrentBuf); /* bit 7 =0 */
|
||||||
|
GWEN_Buffer_AppendBytes(resultBuf, GWEN_Buffer_GetStart(currentBuf), numCurrentBuf);
|
||||||
|
GWEN_Buffer_Reset(currentBuf);
|
||||||
|
}
|
||||||
|
GWEN_Buffer_AppendByte(resultBuf, countRepeats | 128);
|
||||||
|
GWEN_Buffer_AppendByte(resultBuf, *ptrPixels);
|
||||||
|
ptrPixels+=countRepeats;
|
||||||
|
lenPixels-=countRepeats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numCurrentBuf=GWEN_Buffer_GetUsedBytes(currentBuf);
|
||||||
|
if (numCurrentBuf>0) {
|
||||||
|
GWEN_Buffer_AppendByte(resultBuf, numCurrentBuf); /* bit 7 =0 */
|
||||||
|
GWEN_Buffer_AppendBytes(resultBuf, GWEN_Buffer_GetStart(currentBuf), numCurrentBuf);
|
||||||
|
}
|
||||||
|
GWEN_Buffer_free(currentBuf);
|
||||||
|
|
||||||
|
if (GWEN_Buffer_GetUsedBytes(resultBuf)==0) {
|
||||||
|
GWEN_Buffer_free(resultBuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _countRepeats(const uint8_t *ptrPixels, int lenPixels)
|
||||||
|
{
|
||||||
|
if (ptrPixels && lenPixels) {
|
||||||
|
int currentByte;
|
||||||
|
int count=1;
|
||||||
|
|
||||||
|
currentByte=*(ptrPixels++);
|
||||||
|
lenPixels--;
|
||||||
|
while(ptrPixels && lenPixels) {
|
||||||
|
if (*ptrPixels==currentByte) {
|
||||||
|
count++;
|
||||||
|
if (count==127)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return count;
|
||||||
|
|
||||||
|
ptrPixels++;
|
||||||
|
lenPixels--;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_FILE *BMP_File_new(const char *fname)
|
||||||
|
{
|
||||||
|
BMP_FILE *bf;
|
||||||
|
|
||||||
|
GWEN_NEW_OBJECT(BMP_FILE, bf);
|
||||||
|
bf->filename=fname?strdup(fname):NULL;
|
||||||
|
return bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void BMP_File_free(BMP_FILE *bf)
|
||||||
|
{
|
||||||
|
if (bf) {
|
||||||
|
free(bf->filename);
|
||||||
|
_imageHeader_free(bf->imageHeader);
|
||||||
|
_fileHeader_free(bf->fileHeader);
|
||||||
|
GWEN_Buffer_free(bf->buffer);
|
||||||
|
GWEN_FREE_OBJECT(bf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_FILE *BMP_File_fromFile(const char *fname)
|
||||||
|
{
|
||||||
|
BMP_FILE *bf;
|
||||||
|
const uint8_t *ptrBuffer;
|
||||||
|
uint32_t lenBuffer;
|
||||||
|
|
||||||
|
bf=BMP_File_new(fname);
|
||||||
|
bf->buffer=_readFileIntoBuffer(fname);
|
||||||
|
if (bf->buffer==NULL) {
|
||||||
|
fprintf(stderr, "Error reading bmp file \"%s\"\n", fname);
|
||||||
|
BMP_File_free(bf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ptrBuffer=(const uint8_t *) GWEN_Buffer_GetStart(bf->buffer);
|
||||||
|
lenBuffer=GWEN_Buffer_GetUsedBytes(bf->buffer);
|
||||||
|
|
||||||
|
bf->fileHeader=_readFileHeaderAt(ptrBuffer, lenBuffer, 0);
|
||||||
|
if (bf->fileHeader==NULL) {
|
||||||
|
fprintf(stderr, "Error reading bmp file header from \"%s\"\n", fname);
|
||||||
|
BMP_File_free(bf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bf->imageHeader=_readImageHeaderAt(ptrBuffer, lenBuffer, BMP_FILE_OFFS_IMAGEHEADER);
|
||||||
|
if (bf->imageHeader==NULL) {
|
||||||
|
fprintf(stderr, "Error reading bmp image header from \"%s\"\n", fname);
|
||||||
|
BMP_File_free(bf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_FILEHEADER *_fileHeader_new()
|
||||||
|
{
|
||||||
|
BMP_FILEHEADER *fh;
|
||||||
|
|
||||||
|
GWEN_NEW_OBJECT(BMP_FILEHEADER, fh);
|
||||||
|
return fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _fileHeader_free(BMP_FILEHEADER *fh)
|
||||||
|
{
|
||||||
|
if (fh) {
|
||||||
|
GWEN_FREE_OBJECT(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_IMAGEHEADER *_imageHeader_new()
|
||||||
|
{
|
||||||
|
BMP_IMAGEHEADER *ih;
|
||||||
|
|
||||||
|
GWEN_NEW_OBJECT(BMP_IMAGEHEADER, ih);
|
||||||
|
return ih;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _imageHeader_free(BMP_IMAGEHEADER *ih)
|
||||||
|
{
|
||||||
|
if (ih) {
|
||||||
|
GWEN_FREE_OBJECT(ih);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _dumpBmpFileHeader(const BMP_FILEHEADER *fh)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "BMP File Header:\n");
|
||||||
|
fprintf(stderr, "- file type: %d\n", fh->fileType);
|
||||||
|
fprintf(stderr, "- file size: %d\n", fh->fileSize);
|
||||||
|
fprintf(stderr, "- Pixels at: %08x\n", fh->offsPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void _dumpBmpImageHeader(const BMP_IMAGEHEADER *ih)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "BMP Image Header:\n");
|
||||||
|
fprintf(stderr, "- dims : %d x %d (%d planes, %d bpp)\n", ih->imgWidth, ih->imgHeight, ih->imgPlanes, ih->bitsPerPixel);
|
||||||
|
fprintf(stderr, "- compression : %d\n", ih->compression);
|
||||||
|
fprintf(stderr, "- image size : %d\n", ih->imgSize);
|
||||||
|
fprintf(stderr, "- resolution : %d x %d per meter\n", ih->imgXPixelsPerMeter, ih->imgYPixelsPerMeter);
|
||||||
|
fprintf(stderr, "- used colormap: %d entries (%d important)\n", ih->colorMapUsedEntries, ih->colorMapImportantColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_FILEHEADER *_readFileHeaderAt(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset)
|
||||||
|
{
|
||||||
|
BMP_FILEHEADER *fh;
|
||||||
|
|
||||||
|
if (lenBuffer<(offset+BMP_FILEHEADER_SIZE)) {
|
||||||
|
DBG_ERROR(NULL, "Offset 0x%08x out of boundary", offset);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh=_fileHeader_new();
|
||||||
|
fh->fileType=_readUint16At(ptrBuffer, lenBuffer, offset+BMP_FILEHEADER_OFFS_TYPE, 0);
|
||||||
|
fh->fileSize=_readUint32At(ptrBuffer, lenBuffer, offset+BMP_FILEHEADER_OFFS_FSIZE, 0);
|
||||||
|
fh->offsPixels=_readUint32At(ptrBuffer, lenBuffer, offset+BMP_FILEHEADER_OFFS_PIXELOFFS, 0xffffffff);
|
||||||
|
|
||||||
|
return fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BMP_IMAGEHEADER *_readImageHeaderAt(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset)
|
||||||
|
{
|
||||||
|
BMP_IMAGEHEADER *ih;
|
||||||
|
uint32_t hsize;
|
||||||
|
|
||||||
|
hsize=_readUint32At(ptrBuffer, lenBuffer, BMP_FILE_OFFS_IMAGEHEADER, 0xffffffff);
|
||||||
|
if (lenBuffer<(offset+hsize)) {
|
||||||
|
DBG_ERROR(NULL, "Offset 0x%08x out of boundary", offset);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ih=_imageHeader_new();
|
||||||
|
ih->imgWidth=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_WIDTH, 0);
|
||||||
|
ih->imgHeight=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_HEIGHT, 0);
|
||||||
|
ih->imgPlanes=_readUint16At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_PLANES, 0);
|
||||||
|
ih->bitsPerPixel=_readUint16At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_BPP, 0);
|
||||||
|
ih->compression=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_COMPR, 0);
|
||||||
|
ih->imgSize=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_IMGSIZE, 0);
|
||||||
|
ih->imgXPixelsPerMeter=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_XPPM, 0);
|
||||||
|
ih->imgYPixelsPerMeter=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_YPPM, 0);
|
||||||
|
ih->colorMapUsedEntries=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_COLORMAPENTRIES, 0);
|
||||||
|
ih->colorMapImportantColors=_readInt32At(ptrBuffer, lenBuffer, offset+BMP_IMAGEHEADER_OFFS_IMPORTANT, 0);
|
||||||
|
|
||||||
|
return ih;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t _readUint16At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, uint16_t defaultValue)
|
||||||
|
{
|
||||||
|
uint16_t v;
|
||||||
|
|
||||||
|
if (lenBuffer<(offset+2)) {
|
||||||
|
DBG_ERROR(NULL, "Offset 0x%08x out of boundary", offset);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
v=ptrBuffer[offset]+(ptrBuffer[offset+1]<<8);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t _readUint32At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, uint32_t defaultValue)
|
||||||
|
{
|
||||||
|
uint32_t v;
|
||||||
|
|
||||||
|
if (lenBuffer<(offset+4)) {
|
||||||
|
DBG_ERROR(NULL, "Offset 0x%08x out of boundary", offset);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
v=ptrBuffer[offset]+(ptrBuffer[offset+1]<<8)+(ptrBuffer[offset+2]<<16)+(ptrBuffer[offset+2]<<16);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int32_t _readInt32At(const uint8_t *ptrBuffer, uint32_t lenBuffer, uint32_t offset, int32_t defaultValue)
|
||||||
|
{
|
||||||
|
uint32_t v;
|
||||||
|
|
||||||
|
if (lenBuffer<(offset+4)) {
|
||||||
|
DBG_ERROR(NULL, "Offset 0x%08x out of boundary", offset);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
v=ptrBuffer[offset]+(ptrBuffer[offset+1]<<8)+(ptrBuffer[offset+2]<<16)+(ptrBuffer[offset+2]<<16);
|
||||||
|
return (int32_t) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_BUFFER *_readFileIntoBuffer(const char *fname)
|
||||||
|
{
|
||||||
|
GWEN_BUFFER *buf;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
buf=GWEN_Buffer_new(0, 1024, 0, 1);
|
||||||
|
rv=GWEN_SyncIo_Helper_ReadFile(fname, buf);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_ERROR(NULL, "Error reading file \"%s\": %d", fname?fname:"<empty>", rv);
|
||||||
|
GWEN_Buffer_free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
69
apps/aqhome-tool/image/readbmp.h
Normal file
69
apps/aqhome-tool/image/readbmp.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef AQHOME_TOOL_READBMP_H
|
||||||
|
#define AQHOME_TOOL_READBMP_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <gwenhywfar/db.h>
|
||||||
|
#include <gwenhywfar/endpoint.h>
|
||||||
|
|
||||||
|
#include <aqhome/data/value.h>
|
||||||
|
#include <aqhome/data/device.h>
|
||||||
|
|
||||||
|
#include <aqhome/events2/eventloop.h>
|
||||||
|
#include <aqhome/events2/object.h>
|
||||||
|
#include <aqhome/ipc2/message.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct BMP_FILEHEADER BMP_FILEHEADER;
|
||||||
|
struct BMP_FILEHEADER {
|
||||||
|
uint16_t fileType;
|
||||||
|
uint32_t fileSize;
|
||||||
|
uint32_t offsPixels;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct BMP_IMAGEHEADER BMP_IMAGEHEADER;
|
||||||
|
struct BMP_IMAGEHEADER {
|
||||||
|
int32_t imgWidth;
|
||||||
|
int32_t imgHeight;
|
||||||
|
uint16_t imgPlanes; /* 1 */
|
||||||
|
uint16_t bitsPerPixel;
|
||||||
|
uint32_t compression;
|
||||||
|
uint32_t imgSize;
|
||||||
|
uint32_t imgXPixelsPerMeter;
|
||||||
|
uint32_t imgYPixelsPerMeter;
|
||||||
|
uint32_t colorMapUsedEntries;
|
||||||
|
uint32_t colorMapImportantColors;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct BMP_FILE BMP_FILE;
|
||||||
|
struct BMP_FILE {
|
||||||
|
char *filename;
|
||||||
|
BMP_FILEHEADER *fileHeader;
|
||||||
|
BMP_IMAGEHEADER *imageHeader;
|
||||||
|
|
||||||
|
GWEN_BUFFER *buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int AQH_Tool_DumpBmpFile(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
|
||||||
|
|
||||||
|
|
||||||
|
BMP_FILE *BMP_File_new(const char *fname);
|
||||||
|
void BMP_File_free(BMP_FILE *bf);
|
||||||
|
BMP_FILE *BMP_File_fromFile(const char *fname);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* This file is part of the project AqHome.
|
* This file is part of the project AqHome.
|
||||||
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
|
||||||
*
|
*
|
||||||
* The license for this file can be found in the file COPYING which you
|
* The license for this file can be found in the file COPYING which you
|
||||||
* should have received along with this file.
|
* should have received along with this file.
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "./nodes/ping.h"
|
#include "./nodes/ping.h"
|
||||||
#include "./nodes/flash.h"
|
#include "./nodes/flash.h"
|
||||||
|
#include "./nodes/settime.h"
|
||||||
#include "./nodes/getnodes.h"
|
#include "./nodes/getnodes.h"
|
||||||
#include "./data/getvalues.h"
|
#include "./data/getvalues.h"
|
||||||
#include "./data/getdevices.h"
|
#include "./data/getdevices.h"
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
#include "./data/watch.h"
|
#include "./data/watch.h"
|
||||||
#include "./data/devicestate.h"
|
#include "./data/devicestate.h"
|
||||||
#include "./data/imgperioddata.h"
|
#include "./data/imgperioddata.h"
|
||||||
|
#include "./image/readbmp.h"
|
||||||
|
|
||||||
#include <aqhome/api.h>
|
#include <aqhome/api.h>
|
||||||
#include <aqhome/aqhome.h>
|
#include <aqhome/aqhome.h>
|
||||||
@@ -90,6 +92,7 @@ int main(int argc, char **argv)
|
|||||||
};
|
};
|
||||||
const GWEN_FUNCS cmdDefArray[]= {
|
const GWEN_FUNCS cmdDefArray[]= {
|
||||||
GWEN_FE_DAH("ping", AQH_Tool_Ping, I18N("Ping a given node on the network")),
|
GWEN_FE_DAH("ping", AQH_Tool_Ping, I18N("Ping a given node on the network")),
|
||||||
|
GWEN_FE_DAH("time", AQH_Tool_SetTime, I18N("Set node network time (handled by RTC module)")),
|
||||||
GWEN_FE_DAH("flash", AQH_Tool_Flash, I18N("Flash a given node on the network")),
|
GWEN_FE_DAH("flash", AQH_Tool_Flash, I18N("Flash a given node on the network")),
|
||||||
GWEN_FE_DAH("getnodes", AQH_Tool_GetNodes, I18N("Request list of known devices on the network")),
|
GWEN_FE_DAH("getnodes", AQH_Tool_GetNodes, I18N("Request list of known devices on the network")),
|
||||||
GWEN_FE_DAH("getvalues", AQH_Tool_GetValues, I18N("Request list of known values on the data server")),
|
GWEN_FE_DAH("getvalues", AQH_Tool_GetValues, I18N("Request list of known values on the data server")),
|
||||||
@@ -105,6 +108,7 @@ int main(int argc, char **argv)
|
|||||||
GWEN_FE_DAH("watch", AQH_Tool_Watch, I18N("Watch and print changes of values 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("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_DAH("imgperioddata", AQH_Tool_ImgPeriodData, I18N("Create diagram of datapoints from a date range")),
|
||||||
|
GWEN_FE_DAH("dumpbmp", AQH_Tool_DumpBmpFile, I18N("Dump headers of BMP file")),
|
||||||
GWEN_FE_END(),
|
GWEN_FE_END(),
|
||||||
};
|
};
|
||||||
const GWEN_FUNCS *func;
|
const GWEN_FUNCS *func;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
ping.h
|
ping.h
|
||||||
flash.h
|
flash.h
|
||||||
getnodes.h
|
getnodes.h
|
||||||
|
settime.h
|
||||||
</headers>
|
</headers>
|
||||||
|
|
||||||
<sources>
|
<sources>
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
ping.c
|
ping.c
|
||||||
flash.c
|
flash.c
|
||||||
getnodes.c
|
getnodes.c
|
||||||
|
settime.c
|
||||||
</sources>
|
</sources>
|
||||||
|
|
||||||
<useTargets>
|
<useTargets>
|
||||||
|
|||||||
139
apps/aqhome-tool/nodes/settime.c
Normal file
139
apps/aqhome-tool/nodes/settime.c
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2026 Martin Preuss, 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 "./settime.h"
|
||||||
|
#include "../client.h"
|
||||||
|
#include "../utils.h"
|
||||||
|
|
||||||
|
#include "aqhome/msg/ipc/m_ipc.h"
|
||||||
|
#include "aqhome/msg/ipc/m_ipc_result.h"
|
||||||
|
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
|
||||||
|
#include "aqhome/msg/ipc/nodes/m_ipcn_forward.h"
|
||||||
|
#include "aqhome/msg/node/m_node.h"
|
||||||
|
#include "aqhome/msg/node/m_time.h"
|
||||||
|
|
||||||
|
#include <gwenhywfar/args.h>
|
||||||
|
#include <gwenhywfar/i18n.h>
|
||||||
|
#include <gwenhywfar/debug.h>
|
||||||
|
#include <gwenhywfar/text.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 AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
|
||||||
|
static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------
|
||||||
|
* implementations
|
||||||
|
* ------------------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
int AQH_Tool_SetTime(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
|
||||||
|
{
|
||||||
|
AQH_EVENT_LOOP *eventLoop;
|
||||||
|
AQH_OBJECT *o;
|
||||||
|
int rv;
|
||||||
|
const GWEN_ARGS args[]= {
|
||||||
|
/* flags type name min max s long short_descr, long_descr */
|
||||||
|
{ A_ARG, A_CHAR, "tcpAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
|
||||||
|
{ A_ARG, A_INT, "tcpPort", 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, "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, "nodeAddr", 1, 1, "n", "nodeaddr", I18S("Specify bus addr of the node to ping"), NULL},
|
||||||
|
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
eventLoop=AQH_EventLoop_new();
|
||||||
|
o=AQH_ToolClient_new(eventLoop, AQH_IPC_PROTOCOL_NODES_ID, AQH_IPC_PROTOCOL_NODES_VERSION, dbGlobalArgs, args);
|
||||||
|
AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage);
|
||||||
|
AQH_ToolClient_SetHandleResponseMessageFn(o, _handleResponseMessage);
|
||||||
|
rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv);
|
||||||
|
if (rv!=0)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
rv=AQH_ToolClient_Connect(o, AQH_TOOL_CLIENT_CONNECTFLAGS_WITHCONNECTMSG | AQH_TOOL_CLIENT_CONNECTFLAGS_WITHGRPMSG,
|
||||||
|
0, AQH_MSG_TYPEGROUP_ALL);
|
||||||
|
if (rv<0) {
|
||||||
|
DBG_INFO(NULL, "here(%d)", rv);
|
||||||
|
AQH_Object_free(o);
|
||||||
|
AQH_EventLoop_free(eventLoop);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv=AQH_ToolClient_RunConnectedWithNodeMsgs(o);
|
||||||
|
AQH_Object_free(o);
|
||||||
|
AQH_EventLoop_free(eventLoop);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, GWEN_UNUSED uint32_t msgId)
|
||||||
|
{
|
||||||
|
AQH_MESSAGE *nodeMsg;
|
||||||
|
GWEN_DB_NODE *dbArgs;
|
||||||
|
int nodeAddr;
|
||||||
|
GWEN_TIMESTAMP *ts;
|
||||||
|
|
||||||
|
dbArgs=AQH_ToolClient_GetDbLocalArgs(o);
|
||||||
|
nodeAddr=GWEN_DB_GetIntValue(dbArgs, "nodeAddr", 0, 0);
|
||||||
|
ts=GWEN_Timestamp_NowInLocalTime();
|
||||||
|
|
||||||
|
nodeMsg=AQH_TimeMessage_new(nodeAddr, AQH_TOOL_CLIENT_NODEADDR, AQH_MSG_TYPE_TIME_REQSET, 0, ts);
|
||||||
|
return nodeMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, GWEN_UNUSED const GWEN_TAG16_LIST *tagList, GWEN_UNUSED int first)
|
||||||
|
{
|
||||||
|
GWEN_DB_NODE *dbArgs;
|
||||||
|
uint16_t code;
|
||||||
|
int nodeAddr;
|
||||||
|
|
||||||
|
dbArgs=AQH_ToolClient_GetDbLocalArgs(o);
|
||||||
|
nodeAddr=GWEN_DB_GetIntValue(dbArgs, "nodeAddr", 0, 0);
|
||||||
|
|
||||||
|
code=AQH_NodeMessage_GetMsgType(msg);
|
||||||
|
if ((code==AQH_MSG_TYPE_TIME_RSPSET) &&
|
||||||
|
(nodeAddr==0 || nodeAddr==0xff || nodeAddr==AQH_NodeMessage_GetSourceAddress(msg))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "Unexpected message \"%d\"", code);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* This file is part of the project AqHome.
|
* This file is part of the project AqHome.
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
|
||||||
*
|
*
|
||||||
* The license for this file can be found in the file COPYING which you
|
* The license for this file can be found in the file COPYING which you
|
||||||
* should have received along with this file.
|
* should have received along with this file.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifndef AQHOME_STORAGE_HTTP_H
|
#ifndef AQHOME_TOOL_SETTIME_H
|
||||||
#define AQHOME_STORAGE_HTTP_H
|
#define AQHOME_TOOL_SETTIME_H
|
||||||
|
|
||||||
|
|
||||||
#include "./aqhomestorage.h"
|
#include <gwenhywfar/db.h>
|
||||||
|
|
||||||
|
|
||||||
void AqHomeStorage_ReadAndHandleHttpMessages(AQHOME_STORAGE *aqh);
|
int AQH_Tool_SetTime(GWEN_DB_NODE *dbArgs, int argc, char **argv);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
<devices>
|
|
||||||
<device id="plug01" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota/plug01/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota/*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota/</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota/</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="plug02" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota/plug02/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota/*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota/</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota/</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="plug03" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota/plug03/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota/*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota/</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota/</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="109C2F" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota_109C2F/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota_*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota_</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota/</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="10359B" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota_10359B/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota_*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota_</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota_</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="0F25C2" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota_0F25C2/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota_*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota_</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota_</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
<device id="10A4B7" name="tasmotaplug" driver="mqtt">
|
|
||||||
<mqtttopics>
|
|
||||||
<mqtttopic type="json" direction="in" name="sensor">
|
|
||||||
<topic>tele/tasmota_10A4B7/SENSOR</topic>
|
|
||||||
<mask>tele/tasmota_*/SENSOR</mask>
|
|
||||||
<beforeId>tele/tasmota_</beforeId>
|
|
||||||
<afterId>/SENSOR</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="powerusage" units="W" path="ENERGY/POWER" type="sensor"></value>
|
|
||||||
<value name="totalenergy" units="kWh" path="ENERGY/TOTAL" type="sensor"></value>
|
|
||||||
<value name="apparentpower" units="VA" path="ENERGY/ApparentPower" type="sensor"></value>
|
|
||||||
<value name="reactivepower" units="VAr" path="ENERGY/ReactivePower" type="sensor"></value>
|
|
||||||
<value name="powerfactor" path="ENERGY/Factor" type="sensor"></value>
|
|
||||||
<value name="voltage" units="V" path="ENERGY/Voltage" type="sensor"></value>
|
|
||||||
<value name="current" units="A" path="ENERGY/Current" type="sensor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
<mqtttopic type="num" direction="out" name="power">
|
|
||||||
<beforeId>cmnd/tasmota_</beforeId>
|
|
||||||
<afterId>/Power</afterId>
|
|
||||||
<values>
|
|
||||||
<value name="power" type="actor"></value>
|
|
||||||
</values>
|
|
||||||
</mqtttopic>
|
|
||||||
</mqtttopics>
|
|
||||||
</device>
|
|
||||||
</devices>
|
|
||||||
238
aqhome-react.vg
238
aqhome-react.vg
@@ -1,238 +0,0 @@
|
|||||||
==2732905== Memcheck, a memory error detector
|
|
||||||
==2732905== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
|
|
||||||
==2732905== Using Valgrind-3.19.0-8d3c8034b8-20220411 and LibVEX; rerun with -h for copyright info
|
|
||||||
==2732905== Command: 0-build/apps/aqhome-react/aqhome-react -p ./aqhome-react.pid -V ./aqhome-react.vars -ba 127.0.0.1 -T 350
|
|
||||||
==2732905== Parent PID: 2732904
|
|
||||||
==2732905==
|
|
||||||
--2732905--
|
|
||||||
--2732905-- Valgrind options:
|
|
||||||
--2732905-- --tool=memcheck
|
|
||||||
--2732905-- --trace-children=yes
|
|
||||||
--2732905-- -v
|
|
||||||
--2732905-- --log-file=aqhome-react.vg
|
|
||||||
--2732905-- --leak-check=full
|
|
||||||
--2732905-- --show-reachable=yes
|
|
||||||
--2732905-- --track-origins=yes
|
|
||||||
--2732905-- --num-callers=50
|
|
||||||
--2732905-- --keep-stacktraces=alloc-and-free
|
|
||||||
--2732905-- Contents of /proc/version:
|
|
||||||
--2732905-- Linux version 6.1.0-31-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian 6.1.128-1 (2025-02-07)
|
|
||||||
--2732905--
|
|
||||||
--2732905-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-ssse3-avx-avx2-bmi-f16c-rdrand-rdseed
|
|
||||||
--2732905-- Page sizes: currently 4096, max supported 4096
|
|
||||||
--2732905-- Valgrind library directory: /usr/libexec/valgrind
|
|
||||||
--2732905-- Reading syms from /home/martin/projekte/c/0_current/hausbus/aqhome/0-build/apps/aqhome-react/aqhome-react
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/f4/bc47db467906580a47640e01e6d901e2642a7b.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
--2732905-- Reading syms from /usr/libexec/valgrind/memcheck-amd64-linux
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/82/26c2aa6b808ebd5a6fafb694a7fb3287f33590.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
--2732905-- object doesn't have a dynamic symbol table
|
|
||||||
--2732905-- Scheduler: using generic scheduler lock implementation.
|
|
||||||
--2732905-- Reading suppressions file: /usr/libexec/valgrind/default.supp
|
|
||||||
==2732905== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-2732905-by-martin-on-???
|
|
||||||
==2732905== embedded gdbserver: writing to /tmp/vgdb-pipe-to-vgdb-from-2732905-by-martin-on-???
|
|
||||||
==2732905== embedded gdbserver: shared mem /tmp/vgdb-pipe-shared-mem-vgdb-2732905-by-martin-on-???
|
|
||||||
==2732905==
|
|
||||||
==2732905== TO CONTROL THIS PROCESS USING vgdb (which you probably
|
|
||||||
==2732905== don't want to do, unless you know exactly what you're doing,
|
|
||||||
==2732905== or are doing some strange experiment):
|
|
||||||
==2732905== /usr/bin/vgdb --pid=2732905 ...command...
|
|
||||||
==2732905==
|
|
||||||
==2732905== TO DEBUG THIS PROCESS USING GDB: start GDB like this
|
|
||||||
==2732905== /path/to/gdb 0-build/apps/aqhome-react/aqhome-react
|
|
||||||
==2732905== and then give GDB the following command
|
|
||||||
==2732905== target remote | /usr/bin/vgdb --pid=2732905
|
|
||||||
==2732905== --pid is optional if only one valgrind process is running
|
|
||||||
==2732905==
|
|
||||||
--2732905-- REDIR: 0x40238e0 (ld-linux-x86-64.so.2:strlen) redirected to 0x580bb0e2 (vgPlain_amd64_linux_REDIR_FOR_strlen)
|
|
||||||
--2732905-- REDIR: 0x40220c0 (ld-linux-x86-64.so.2:index) redirected to 0x580bb0fc (vgPlain_amd64_linux_REDIR_FOR_index)
|
|
||||||
--2732905-- Reading syms from /usr/libexec/valgrind/vgpreload_core-amd64-linux.so
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/ad/f1388be4d8781737b0c83fe111a5a9c6e930aa.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
--2732905-- Reading syms from /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/d8/ec66cffcb23a75c3f15940674d6028709121f8.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x040238e0 (strlen ) R-> (0000.0) 0x580bb0e2 vgPlain_amd64_linux_REDIR_FOR_strlen
|
|
||||||
--2732905-- new: 0x040238e0 (strlen ) R-> (2007.0) 0x048468a0 strlen
|
|
||||||
--2732905-- REDIR: 0x40222e0 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4847780 (strcmp)
|
|
||||||
--2732905-- REDIR: 0x4021550 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x484b1a0 (mempcpy)
|
|
||||||
--2732905-- Reading syms from /home/martin/projekte/c/0_current/hausbus/aqhome/0-build/aqhome/libaqhome.so.0.0.8
|
|
||||||
--2732905-- Reading syms from /usr/local/lib/libgwenhywfar.so.79.12.0
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libm.so.6
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/6d/201df2cb50847f0ed42da4158c3a608d578f03.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libc.so.6
|
|
||||||
--2732905-- Considering /usr/lib/debug/.build-id/c0/47672cae7964324658491e7dee26748ae5d2f8.debug ..
|
|
||||||
--2732905-- .. build-id is valid
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04bad5d0 (memalign ) R-> (1011.0) 0x04845bc0 memalign
|
|
||||||
--2732905-- new: 0x04bad5d0 (memalign ) R-> (1017.0) 0x04845b90 aligned_alloc
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04bad5d0 (memalign ) R-> (1011.0) 0x04845bc0 memalign
|
|
||||||
--2732905-- new: 0x04bad5d0 (memalign ) R-> (1017.0) 0x04845b60 aligned_alloc
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04bad5d0 (memalign ) R-> (1011.0) 0x04845bc0 memalign
|
|
||||||
--2732905-- new: 0x04bad5d0 (memalign ) R-> (1017.0) 0x04845b90 aligned_alloc
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04bad5d0 (memalign ) R-> (1011.0) 0x04845bc0 memalign
|
|
||||||
--2732905-- new: 0x04bad5d0 (memalign ) R-> (1017.0) 0x04845b60 aligned_alloc
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.33.1
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libgnutls.so.30.34.3
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.8
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libunistring.so.2.2.0
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libtasn1.so.6.6.3
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libnettle.so.8.6
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libhogweed.so.6.6
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libgmp.so.10.4.1
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- Reading syms from /usr/lib/x86_64-linux-gnu/libffi.so.8.1.2
|
|
||||||
--2732905-- object doesn't have a symbol table
|
|
||||||
--2732905-- REDIR: 0x4bb3510 (libc.so.6:strnlen) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb35a0 (libc.so.6:strpbrk) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb16c0 (libc.so.6:strcmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc64a0 (libc.so.6:wcsnlen) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0800 (libc.so.6:memset) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc4de0 (libc.so.6:wcslen) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bafe30 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc4c10 (libc.so.6:wcschr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb15b0 (libc.so.6:index) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb35d0 (libc.so.6:rindex) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc4ca0 (libc.so.6:wcscmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0a40 (libc.so.6:stpncpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc51f0 (libc.so.6:wmemchr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb33c0 (libc.so.6:strncmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0ab0 (libc.so.6:strcasecmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb2920 (libc.so.6:strcspn) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bc4d30 (libc.so.6:wcscpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb1530 (libc.so.6:strcat) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb32b0 (libc.so.6:strncasecmp_l) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bafd40 (libc.so.6:bcmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0770 (libc.so.6:memrchr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb1630 (libc.so.6:strchrnul) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb28a0 (libc.so.6:strcpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0b50 (libc.so.6:strcasecmp_l) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb3180 (libc.so.6:strlen) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb3460 (libc.so.6:strncpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb09c0 (libc.so.6:stpcpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0550 (libc.so.6:memmove) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
==2732905== Preferring higher priority redirection:
|
|
||||||
--2732905-- old: 0x04c66940 (__memcpy_avx_unalign) R-> (2018.0) 0x04848a60 __memcpy_avx_unaligned_erms
|
|
||||||
--2732905-- new: 0x04c66940 (__memcpy_avx_unalign) R-> (2018.1) 0x0484a2b0 memmove
|
|
||||||
--2732905-- REDIR: 0x4bafcc0 (libc.so.6:memchr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb3790 (libc.so.6:strspn) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0660 (libc.so.6:mempcpy) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb3210 (libc.so.6:strncasecmp) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb0900 (libc.so.6:rawmemchr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4bb3350 (libc.so.6:strncat) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4c2a050 (libc.so.6:__memcpy_chk) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
--2732905-- REDIR: 0x4c2a160 (libc.so.6:__memmove_chk) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04c66900 (__memcpy_chk_avx_una) R-> (2030.0) 0x0484b290 __memcpy_chk
|
|
||||||
--2732905-- new: 0x04c66900 (__memcpy_chk_avx_una) R-> (2024.0) 0x0484ac30 __memmove_chk
|
|
||||||
--2732905-- REDIR: 0x4bb3f30 (libc.so.6:strstr) redirected to 0x48371b0 (_vgnU_ifunc_wrapper)
|
|
||||||
==2732905== WARNING: new redirection conflicts with existing -- ignoring it
|
|
||||||
--2732905-- old: 0x04c66900 (__memcpy_chk_avx_una) R-> (2030.0) 0x0484b290 __memcpy_chk
|
|
||||||
--2732905-- new: 0x04c66900 (__memcpy_chk_avx_una) R-> (2024.0) 0x0484ac30 __memmove_chk
|
|
||||||
--2732905-- REDIR: 0x4c6ca10 (libc.so.6:__strrchr_avx2) redirected to 0x48462e0 (rindex)
|
|
||||||
--2732905-- REDIR: 0x4c6a0c0 (libc.so.6:__strlen_avx2) redirected to 0x4846780 (strlen)
|
|
||||||
--2732905-- REDIR: 0x4c661e0 (libc.so.6:__memcmp_avx2_movbe) redirected to 0x4849aa0 (bcmp)
|
|
||||||
--2732905-- REDIR: 0x4c6b790 (libc.so.6:__strncmp_avx2) redirected to 0x4846ed0 (strncmp)
|
|
||||||
--2732905-- REDIR: 0x4c693a0 (libc.so.6:__strchr_avx2) redirected to 0x4846460 (index)
|
|
||||||
--2732905-- REDIR: 0x4c697d0 (libc.so.6:__strcmp_avx2) redirected to 0x4847680 (strcmp)
|
|
||||||
--2732905-- REDIR: 0x4bac8f0 (libc.so.6:malloc) redirected to 0x4840740 (malloc)
|
|
||||||
--2732905-- REDIR: 0x4c66940 (libc.so.6:__memcpy_avx_unaligned_erms) redirected to 0x484a2b0 (memmove)
|
|
||||||
--2732905-- REDIR: 0x4bad6a0 (libc.so.6:calloc) redirected to 0x4845540 (calloc)
|
|
||||||
--2732905-- REDIR: 0x4c66900 (libc.so.6:__memcpy_chk_avx_unaligned_erms) redirected to 0x484b290 (__memcpy_chk)
|
|
||||||
--2732905-- REDIR: 0x4baceb0 (libc.so.6:free) redirected to 0x4843110 (free)
|
|
||||||
--2732905-- REDIR: 0x4c68320 (libc.so.6:__strcasecmp_avx2) redirected to 0x4847050 (strcasecmp)
|
|
||||||
--2732905-- REDIR: 0x4c65f40 (libc.so.6:__memchr_avx2) redirected to 0x4847800 (memchr)
|
|
||||||
--2732905-- REDIR: 0x4bc41b0 (libc.so.6:__strstr_sse2_unaligned) redirected to 0x484b3a0 (strstr)
|
|
||||||
--2732905-- REDIR: 0x4bad0f0 (libc.so.6:realloc) redirected to 0x48457b0 (realloc)
|
|
||||||
--2732905-- REDIR: 0x4c67480 (libc.so.6:__rawmemchr_avx2) redirected to 0x484acd0 (rawmemchr)
|
|
||||||
--2732905-- REDIR: 0x4c695e0 (libc.so.6:__strchrnul_avx2) redirected to 0x484aca0 (strchrnul)
|
|
||||||
--2732905-- REDIR: 0x4c668f0 (libc.so.6:__mempcpy_avx_unaligned_erms) redirected to 0x484adb0 (mempcpy)
|
|
||||||
--2732905-- REDIR: 0x4c67340 (libc.so.6:__memset_avx2_unaligned_erms) redirected to 0x484a1c0 (memset)
|
|
||||||
--2732905-- REDIR: 0x4c675e0 (libc.so.6:__stpcpy_avx2) redirected to 0x4849b60 (stpcpy)
|
|
||||||
--2732905-- REDIR: 0x4c6c740 (libc.so.6:__strnlen_avx2) redirected to 0x4846720 (strnlen)
|
|
||||||
==2732905==
|
|
||||||
==2732905== HEAP SUMMARY:
|
|
||||||
==2732905== in use at exit: 192 bytes in 12 blocks
|
|
||||||
==2732905== total heap usage: 8,221 allocs, 8,209 frees, 1,218,973 bytes allocated
|
|
||||||
==2732905==
|
|
||||||
==2732905== Searching for pointers to 12 not-freed blocks
|
|
||||||
==2732905== Checked 279,376 bytes
|
|
||||||
==2732905==
|
|
||||||
==2732905== 48 bytes in 6 blocks are still reachable in loss record 1 of 2
|
|
||||||
==2732905== at 0x48407B4: malloc (vg_replace_malloc.c:381)
|
|
||||||
==2732905== by 0x4D323C0: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D33B9F: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4E112D6: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D32339: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D333DE: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D2E788: gcry_control (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4943601: GWEN_Crypt3_ModuleInit (cryptkey.c:36)
|
|
||||||
==2732905== by 0x49234C9: GWEN_Init (gwenhywfar.c:265)
|
|
||||||
==2732905== by 0x4921F98: GWEN_LibInit (init.c:48)
|
|
||||||
==2732905== by 0x40049CD: call_init (dl-init.c:74)
|
|
||||||
==2732905== by 0x40049CD: call_init (dl-init.c:26)
|
|
||||||
==2732905== by 0x4004AB3: _dl_init (dl-init.c:121)
|
|
||||||
==2732905== by 0x401AA7F: ??? (in /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
|
|
||||||
==2732905== by 0x8: ???
|
|
||||||
==2732905== by 0x1FFF000212: ???
|
|
||||||
==2732905== by 0x1FFF000239: ???
|
|
||||||
==2732905== by 0x1FFF00023C: ???
|
|
||||||
==2732905== by 0x1FFF00024F: ???
|
|
||||||
==2732905== by 0x1FFF000252: ???
|
|
||||||
==2732905== by 0x1FFF000266: ???
|
|
||||||
==2732905== by 0x1FFF00026A: ???
|
|
||||||
==2732905== by 0x1FFF000274: ???
|
|
||||||
==2732905== by 0x1FFF000277: ???
|
|
||||||
==2732905==
|
|
||||||
==2732905== 144 bytes in 6 blocks are still reachable in loss record 2 of 2
|
|
||||||
==2732905== at 0x48407B4: malloc (vg_replace_malloc.c:381)
|
|
||||||
==2732905== by 0x4D323C0: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D33B9F: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4E112C9: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D32339: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D333DE: ??? (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4D2E788: gcry_control (in /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.4.1)
|
|
||||||
==2732905== by 0x4943601: GWEN_Crypt3_ModuleInit (cryptkey.c:36)
|
|
||||||
==2732905== by 0x49234C9: GWEN_Init (gwenhywfar.c:265)
|
|
||||||
==2732905== by 0x4921F98: GWEN_LibInit (init.c:48)
|
|
||||||
==2732905== by 0x40049CD: call_init (dl-init.c:74)
|
|
||||||
==2732905== by 0x40049CD: call_init (dl-init.c:26)
|
|
||||||
==2732905== by 0x4004AB3: _dl_init (dl-init.c:121)
|
|
||||||
==2732905== by 0x401AA7F: ??? (in /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
|
|
||||||
==2732905== by 0x8: ???
|
|
||||||
==2732905== by 0x1FFF000212: ???
|
|
||||||
==2732905== by 0x1FFF000239: ???
|
|
||||||
==2732905== by 0x1FFF00023C: ???
|
|
||||||
==2732905== by 0x1FFF00024F: ???
|
|
||||||
==2732905== by 0x1FFF000252: ???
|
|
||||||
==2732905== by 0x1FFF000266: ???
|
|
||||||
==2732905== by 0x1FFF00026A: ???
|
|
||||||
==2732905== by 0x1FFF000274: ???
|
|
||||||
==2732905== by 0x1FFF000277: ???
|
|
||||||
==2732905==
|
|
||||||
==2732905== LEAK SUMMARY:
|
|
||||||
==2732905== definitely lost: 0 bytes in 0 blocks
|
|
||||||
==2732905== indirectly lost: 0 bytes in 0 blocks
|
|
||||||
==2732905== possibly lost: 0 bytes in 0 blocks
|
|
||||||
==2732905== still reachable: 192 bytes in 12 blocks
|
|
||||||
==2732905== suppressed: 0 bytes in 0 blocks
|
|
||||||
==2732905==
|
|
||||||
==2732905== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export AQHOME_LOGLEVEL=info
|
|
||||||
export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH"
|
|
||||||
|
|
||||||
# 0-build/apps/aqhome-storage/aqhome-storage $*
|
|
||||||
|
|
||||||
0-build/apps/aqhome-storage/aqhome-storage \
|
|
||||||
--sourcefolder=apps/aqhome-storage/test/html \
|
|
||||||
--datafolder=apps/aqhome-storage/test/data \
|
|
||||||
-D apps/aqhome-storage/test/config \
|
|
||||||
--statefile=apps/aqhome-storage/test/config/state \
|
|
||||||
-ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMESTORAGETEST \
|
|
||||||
-ha 127.0.0.1 -hp 1884 \
|
|
||||||
-p ./aqhome-storage.pid \
|
|
||||||
"$@"
|
|
||||||
@@ -41,6 +41,7 @@ static void _signalReadyFdObjects(AQH_OBJECT_LIST2 *ol);
|
|||||||
|
|
||||||
void AQH_EventLoop_Run(AQH_EVENT_LOOP *eventLoop, int timeoutInMillisecs)
|
void AQH_EventLoop_Run(AQH_EVENT_LOOP *eventLoop, int timeoutInMillisecs)
|
||||||
{
|
{
|
||||||
|
static int selectErrorCount=0;
|
||||||
fd_set fdRead;
|
fd_set fdRead;
|
||||||
fd_set fdWrite;
|
fd_set fdWrite;
|
||||||
int highestFd=-1;
|
int highestFd=-1;
|
||||||
@@ -61,6 +62,8 @@ void AQH_EventLoop_Run(AQH_EVENT_LOOP *eventLoop, int timeoutInMillisecs)
|
|||||||
rv=select(highestFd+1, &fdRead, &fdWrite, NULL, &tv);
|
rv=select(highestFd+1, &fdRead, &fdWrite, NULL, &tv);
|
||||||
if (rv>0) {
|
if (rv>0) {
|
||||||
/* some fds became active */
|
/* some fds became active */
|
||||||
|
if (selectErrorCount)
|
||||||
|
selectErrorCount--;
|
||||||
_markReadyFdObjects(eventLoop->fdObjectList, &fdRead, AQH_FDOBJECT_FDMODE_READ);
|
_markReadyFdObjects(eventLoop->fdObjectList, &fdRead, AQH_FDOBJECT_FDMODE_READ);
|
||||||
_markReadyFdObjects(eventLoop->fdObjectList, &fdWrite, AQH_FDOBJECT_FDMODE_WRITE);
|
_markReadyFdObjects(eventLoop->fdObjectList, &fdWrite, AQH_FDOBJECT_FDMODE_WRITE);
|
||||||
_signalReadyFdObjects(eventLoop->fdObjectList);
|
_signalReadyFdObjects(eventLoop->fdObjectList);
|
||||||
@@ -68,11 +71,24 @@ void AQH_EventLoop_Run(AQH_EVENT_LOOP *eventLoop, int timeoutInMillisecs)
|
|||||||
else if (rv<0) {
|
else if (rv<0) {
|
||||||
if (errno!=EINTR) {
|
if (errno!=EINTR) {
|
||||||
/* error */
|
/* error */
|
||||||
DBG_ERROR(AQH_LOGDOMAIN, "Error on SELECT: %d (%s)", errno, strerror(errno));
|
DBG_ERROR(AQH_LOGDOMAIN, "Error on SELECT: %d (%s)", errno, strerror(errno));
|
||||||
|
selectErrorCount++;
|
||||||
|
/* TODO: walk through all sockets to find the bad one */
|
||||||
|
if (selectErrorCount==90) {
|
||||||
|
/* increase log level */
|
||||||
|
DBG_ERROR(AQH_LOGDOMAIN, "Increasing log level to INFO");
|
||||||
|
GWEN_Logger_SetLevel(AQH_LOGDOMAIN, GWEN_LoggerLevel_Info);
|
||||||
|
}
|
||||||
|
else if (selectErrorCount==100) {
|
||||||
|
DBG_ERROR(AQH_LOGDOMAIN, "Aborting due to too many SELECT errors, will be restarted by systemd");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* no fd became active (TODO: maybe signal deep idle objects?) */
|
/* no fd became active (TODO: maybe signal deep idle objects?) */
|
||||||
|
if (selectErrorCount)
|
||||||
|
selectErrorCount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
m_flashready.h
|
m_flashready.h
|
||||||
m_flashresponse.h
|
m_flashresponse.h
|
||||||
m_range.h
|
m_range.h
|
||||||
|
m_time.h
|
||||||
</headers>
|
</headers>
|
||||||
|
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
m_flashready.c
|
m_flashready.c
|
||||||
m_flashresponse.c
|
m_flashresponse.c
|
||||||
m_range.c
|
m_range.c
|
||||||
|
m_time.c
|
||||||
</sources>
|
</sources>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,18 @@
|
|||||||
|
|
||||||
|
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_UID 0 /* 4 bytes */
|
#define AQH_MSG_OFFS_MEMSTATS_UID 0 /* 4 bytes */
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_SECONDS 4 /* 4 bytes */
|
#define AQH_MSG_OFFS_MEMSTATS_UPHOURS 4 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_UPMINS 5 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_UPSECS 6 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_UPTICKS 7 /* 1 byte */
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_STACKUSAGE 8 /* 2 bytes */
|
#define AQH_MSG_OFFS_MEMSTATS_STACKUSAGE 8 /* 2 bytes */
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_BUFFERSUSED 10 /* 1 byte */
|
#define AQH_MSG_OFFS_MEMSTATS_BUFFERSUSED 10 /* 1 byte */
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_MAXBUFFERSUSED 11 /* 1 byte */
|
#define AQH_MSG_OFFS_MEMSTATS_MAXBUFFERSUSED 11 /* 1 byte */
|
||||||
#define AQH_MSG_OFFS_MEMSTATS_RECVNOBUFFER 12 /* 2 bytes */
|
#define AQH_MSG_OFFS_MEMSTATS_RECVNOBUFFER 12 /* 2 bytes */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_HEAPUSED 14 /* 2 bytes */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_HEAPFREE 16 /* 2 bytes */
|
||||||
|
#define AQH_MSG_OFFS_MEMSTATS_XRAMSIZE 18 /* 2 bytes */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -35,12 +42,34 @@ uint32_t AQH_MemStatsMessage_GetUid(const AQH_MESSAGE *msg)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t AQH_MemStatsMessage_GetSeconds(const AQH_MESSAGE *msg)
|
uint8_t AQH_MemStatsMessage_GetUptimeHours(const AQH_MESSAGE *msg)
|
||||||
{
|
{
|
||||||
return AQH_Message_ReadUint32At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_SECONDS, 0);
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_UPHOURS, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_MemStatsMessage_GetUptimeMinutes(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_UPMINS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_MemStatsMessage_GetUptimeSeconds(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_UPSECS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_MemStatsMessage_GetUptimeTicks(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_UPTICKS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint16_t AQH_MemStatsMessage_GetStackUsage(const AQH_MESSAGE *msg)
|
uint16_t AQH_MemStatsMessage_GetStackUsage(const AQH_MESSAGE *msg)
|
||||||
{
|
{
|
||||||
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_STACKUSAGE, 0);
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_STACKUSAGE, 0);
|
||||||
@@ -69,20 +98,49 @@ uint16_t AQH_MemStatsMessage_GetRecvNoBufferErrors(const AQH_MESSAGE *msg)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t AQH_MemStatsMessage_GetHeapUsed(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_HEAPUSED, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t AQH_MemStatsMessage_GetHeapFree(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_HEAPFREE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t AQH_MemStatsMessage_GetXramSize(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_MEMSTATS_XRAMSIZE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AQH_MemStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
|
void AQH_MemStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
|
||||||
{
|
{
|
||||||
GWEN_Buffer_AppendArgs(dbuf,
|
GWEN_Buffer_AppendArgs(dbuf,
|
||||||
"0x%02x->0x%02x: MEMSTATS(%s) %s (uid=0x%08x, uptime=%d, stack used=%d, buffers used=%d(max=%d), no recvbuf=%d)\n",
|
"0x%02x->0x%02x: MEMSTATS(%s) %s (uid=0x%08x, up=%dh:%dm:%ds:%dt, "
|
||||||
|
"stack used=%d, buffers used=%d(max=%d), no recvbuf=%d, "
|
||||||
|
"heap used=%d, heap free=%d, xram=%d)\n",
|
||||||
AQH_NodeMessage_GetSourceAddress(msg),
|
AQH_NodeMessage_GetSourceAddress(msg),
|
||||||
AQH_NodeMessage_GetDestAddress(msg),
|
AQH_NodeMessage_GetDestAddress(msg),
|
||||||
AQH_NodeMessage_MsgTypeToChar(AQH_NodeMessage_GetMsgType(msg)),
|
AQH_NodeMessage_MsgTypeToChar(AQH_NodeMessage_GetMsgType(msg)),
|
||||||
sText,
|
sText,
|
||||||
(unsigned int) AQH_MemStatsMessage_GetUid(msg),
|
(unsigned int) AQH_MemStatsMessage_GetUid(msg),
|
||||||
AQH_MemStatsMessage_GetSeconds(msg),
|
AQH_MemStatsMessage_GetUptimeHours(msg),
|
||||||
|
AQH_MemStatsMessage_GetUptimeMinutes(msg),
|
||||||
|
AQH_MemStatsMessage_GetUptimeSeconds(msg),
|
||||||
|
AQH_MemStatsMessage_GetUptimeTicks(msg),
|
||||||
AQH_MemStatsMessage_GetStackUsage(msg),
|
AQH_MemStatsMessage_GetStackUsage(msg),
|
||||||
AQH_MemStatsMessage_GetBuffersUsed(msg),
|
AQH_MemStatsMessage_GetBuffersUsed(msg),
|
||||||
AQH_MemStatsMessage_GetMaxBuffersUsed(msg),
|
AQH_MemStatsMessage_GetMaxBuffersUsed(msg),
|
||||||
AQH_MemStatsMessage_GetRecvNoBufferErrors(msg));
|
AQH_MemStatsMessage_GetRecvNoBufferErrors(msg),
|
||||||
|
AQH_MemStatsMessage_GetHeapUsed(msg),
|
||||||
|
AQH_MemStatsMessage_GetHeapFree(msg),
|
||||||
|
AQH_MemStatsMessage_GetXramSize(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,17 @@
|
|||||||
|
|
||||||
|
|
||||||
AQHOME_API uint32_t AQH_MemStatsMessage_GetUid(const AQH_MESSAGE *msg);
|
AQHOME_API uint32_t AQH_MemStatsMessage_GetUid(const AQH_MESSAGE *msg);
|
||||||
AQHOME_API uint32_t AQH_MemStatsMessage_GetSeconds(const AQH_MESSAGE *msg);
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetUptimeHours(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetUptimeMinutes(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetUptimeSeconds(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetUptimeTicks(const AQH_MESSAGE *msg);
|
||||||
AQHOME_API uint16_t AQH_MemStatsMessage_GetStackUsage(const AQH_MESSAGE *msg);
|
AQHOME_API uint16_t AQH_MemStatsMessage_GetStackUsage(const AQH_MESSAGE *msg);
|
||||||
AQHOME_API uint8_t AQH_MemStatsMessage_GetBuffersUsed(const AQH_MESSAGE *msg);
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetBuffersUsed(const AQH_MESSAGE *msg);
|
||||||
AQHOME_API uint8_t AQH_MemStatsMessage_GetMaxBuffersUsed(const AQH_MESSAGE *msg);
|
AQHOME_API uint8_t AQH_MemStatsMessage_GetMaxBuffersUsed(const AQH_MESSAGE *msg);
|
||||||
AQHOME_API uint16_t AQH_MemStatsMessage_GetRecvNoBufferErrors(const AQH_MESSAGE *msg);
|
AQHOME_API uint16_t AQH_MemStatsMessage_GetRecvNoBufferErrors(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint16_t AQH_MemStatsMessage_GetHeapUsed(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint16_t AQH_MemStatsMessage_GetHeapFree(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint16_t AQH_MemStatsMessage_GetXramSize(const AQH_MESSAGE *msg);
|
||||||
|
|
||||||
AQHOME_API void AQH_MemStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
|
AQHOME_API void AQH_MemStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,9 @@ const char *AQH_NodeMessage_MsgTypeToChar(uint8_t i)
|
|||||||
case AQH_MSG_TYPE_VALUE_SET: return "ValueSet";
|
case AQH_MSG_TYPE_VALUE_SET: return "ValueSet";
|
||||||
case AQH_MSG_TYPE_VALUE_SET_ACK: return "ValueSetAck";
|
case AQH_MSG_TYPE_VALUE_SET_ACK: return "ValueSetAck";
|
||||||
case AQH_MSG_TYPE_VALUE_SET_NACK: return "ValueSetNack";
|
case AQH_MSG_TYPE_VALUE_SET_NACK: return "ValueSetNack";
|
||||||
|
case AQH_MSG_TYPE_TIME_ANNOUNCE: return "TimeAnnounce";
|
||||||
|
case AQH_MSG_TYPE_TIME_REQSET: return "TimeSetRequest";
|
||||||
|
case AQH_MSG_TYPE_TIME_RSPSET: return "TimeSetResponse";
|
||||||
default: return "(unknown)";
|
default: return "(unknown)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,6 +271,10 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
|
|||||||
case AQH_MSG_TYPE_VALUE_SET_ACK: AQH_ValueMessage_DumpToBuffer(msg, dbuf, sText); break;
|
case AQH_MSG_TYPE_VALUE_SET_ACK: AQH_ValueMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
case AQH_MSG_TYPE_VALUE_SET_NACK: AQH_ValueMessage_DumpToBuffer(msg, dbuf, sText); break;
|
case AQH_MSG_TYPE_VALUE_SET_NACK: AQH_ValueMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
|
|
||||||
|
case AQH_MSG_TYPE_TIME_ANNOUNCE: AQH_TimeMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
|
case AQH_MSG_TYPE_TIME_REQSET: AQH_TimeMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
|
case AQH_MSG_TYPE_TIME_RSPSET: AQH_TimeMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
|
|
||||||
case AQH_MSG_TYPE_DEBUG:
|
case AQH_MSG_TYPE_DEBUG:
|
||||||
case AQH_MSG_TYPE_TWIBUSMEMBER:
|
case AQH_MSG_TYPE_TWIBUSMEMBER:
|
||||||
default: AQH_NodeMessage_DumpToBuffer(msg, dbuf, sText); break;
|
default: AQH_NodeMessage_DumpToBuffer(msg, dbuf, sText); break;
|
||||||
@@ -299,6 +306,11 @@ uint32_t AQH_NodeMessage_GetMsgGroup(uint8_t msgType)
|
|||||||
case AQH_MSG_TYPE_VALUE_SET_NACK:
|
case AQH_MSG_TYPE_VALUE_SET_NACK:
|
||||||
return AQH_MSG_TYPEGROUP_VALUES;
|
return AQH_MSG_TYPEGROUP_VALUES;
|
||||||
|
|
||||||
|
case AQH_MSG_TYPE_TIME_ANNOUNCE:
|
||||||
|
case AQH_MSG_TYPE_TIME_REQSET:
|
||||||
|
case AQH_MSG_TYPE_TIME_RSPSET:
|
||||||
|
return AQH_MSG_TYPEGROUP_TIME;
|
||||||
|
|
||||||
case AQH_MSG_TYPE_NEED_ADDRESS:
|
case AQH_MSG_TYPE_NEED_ADDRESS:
|
||||||
case AQH_MSG_TYPE_HAVE_ADDRESS:
|
case AQH_MSG_TYPE_HAVE_ADDRESS:
|
||||||
case AQH_MSG_TYPE_CLAIM_ADDRESS:
|
case AQH_MSG_TYPE_CLAIM_ADDRESS:
|
||||||
|
|||||||
@@ -60,6 +60,10 @@
|
|||||||
#define AQH_MSG_TYPE_VALUE_SET_ACK 102
|
#define AQH_MSG_TYPE_VALUE_SET_ACK 102
|
||||||
#define AQH_MSG_TYPE_VALUE_SET_NACK 103
|
#define AQH_MSG_TYPE_VALUE_SET_NACK 103
|
||||||
|
|
||||||
|
#define AQH_MSG_TYPE_TIME_ANNOUNCE 120
|
||||||
|
#define AQH_MSG_TYPE_TIME_REQSET 121
|
||||||
|
#define AQH_MSG_TYPE_TIME_RSPSET 122
|
||||||
|
|
||||||
|
|
||||||
/* internal msg types via NET interface */
|
/* internal msg types via NET interface */
|
||||||
#define AQH_MSG_TYPE_NET_SET_ACCEPTED_MSGGROUPS 200
|
#define AQH_MSG_TYPE_NET_SET_ACCEPTED_MSGGROUPS 200
|
||||||
@@ -70,6 +74,7 @@
|
|||||||
#define AQH_MSG_TYPEGROUP_ADDRESS 0x00000004
|
#define AQH_MSG_TYPEGROUP_ADDRESS 0x00000004
|
||||||
#define AQH_MSG_TYPEGROUP_FLASH 0x00000008
|
#define AQH_MSG_TYPEGROUP_FLASH 0x00000008
|
||||||
#define AQH_MSG_TYPEGROUP_ADMIN 0x00000010
|
#define AQH_MSG_TYPEGROUP_ADMIN 0x00000010
|
||||||
|
#define AQH_MSG_TYPEGROUP_TIME 0x00000020
|
||||||
#define AQH_MSG_TYPEGROUP_ALL 0xffffffff
|
#define AQH_MSG_TYPEGROUP_ALL 0xffffffff
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
177
aqhome/msg/node/m_time.c
Normal file
177
aqhome/msg/node/m_time.c
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "aqhome/aqhome.h"
|
||||||
|
#include "aqhome/msg/node/m_time.h"
|
||||||
|
#include "aqhome/msg/node/m_node.h"
|
||||||
|
|
||||||
|
#include <gwenhywfar/debug.h>
|
||||||
|
#include <gwenhywfar/endianfns.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define AQH_MSG_OFFS_TIME_UID 0 /* 4 bytes */
|
||||||
|
#define AQH_MSG_OFFS_TIME_MSGID 4 /* 2 bytes */
|
||||||
|
#define AQH_MSG_OFFS_TIME_YEAR 6 /* 2 bytes */
|
||||||
|
#define AQH_MSG_OFFS_TIME_MONTH 8 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_TIME_DAYOFMONTH 9 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_TIME_DAYOFWEEK 10 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_TIME_HOUR 11 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_TIME_MINUTE 12 /* 1 byte */
|
||||||
|
#define AQH_MSG_OFFS_TIME_SECOND 13 /* 1 byte */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQH_MESSAGE *AQH_TimeMessage_new(uint8_t destAddr, uint8_t srcAddr, uint8_t code, uint16_t msgId, const GWEN_TIMESTAMP *ts)
|
||||||
|
{
|
||||||
|
uint8_t payload[14];
|
||||||
|
uint8_t *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ptr=payload;
|
||||||
|
*(ptr++)=0; /* uid (empty) */
|
||||||
|
*(ptr++)=0;
|
||||||
|
*(ptr++)=0;
|
||||||
|
*(ptr++)=0;
|
||||||
|
|
||||||
|
*(ptr++)=msgId & 0xff; /* msgid */
|
||||||
|
*(ptr++)=(msgId>>8) & 0xff;
|
||||||
|
|
||||||
|
i=GWEN_Timestamp_GetYear(ts); /* year */
|
||||||
|
*(ptr++)=i & 0xff;
|
||||||
|
*(ptr++)=(i>>8) & 0xff;
|
||||||
|
|
||||||
|
*(ptr++)=GWEN_Timestamp_GetMonth(ts) & 0xff; /* month */
|
||||||
|
*(ptr++)=GWEN_Timestamp_GetDay(ts) & 0xff; /* day of month */
|
||||||
|
*(ptr++)=((GWEN_Timestamp_GetWeekDay(ts)+1) & 0xff); /* day of week */
|
||||||
|
*(ptr++)=GWEN_Timestamp_GetHour(ts) & 0xff; /* hour */
|
||||||
|
*(ptr++)=GWEN_Timestamp_GetMinute(ts) & 0xff; /* minute */
|
||||||
|
*(ptr++)=GWEN_Timestamp_GetSecond(ts) & 0xff; /* second */
|
||||||
|
|
||||||
|
return AQH_NodeMessage_new(destAddr, srcAddr, code, sizeof(payload), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t AQH_TimeMessage_GetUid(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint32At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_UID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t AQH_TimeMessage_GetMsgId(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_MSGID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t AQH_TimeMessage_GetYear(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_YEAR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetMonth(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_MONTH, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetDayOfMonth(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_DAYOFMONTH, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetDayOfWeek(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_DAYOFWEEK, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetHour(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_HOUR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetMinute(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_MINUTE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t AQH_TimeMessage_GetSecond(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_TIME_SECOND, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GWEN_TIMESTAMP *AQH_TimeMessage_GetTime(const AQH_MESSAGE *msg)
|
||||||
|
{
|
||||||
|
return GWEN_Timestamp_new(AQH_TimeMessage_GetYear(msg),
|
||||||
|
AQH_TimeMessage_GetMonth(msg),
|
||||||
|
AQH_TimeMessage_GetDayOfMonth(msg),
|
||||||
|
AQH_TimeMessage_GetHour(msg),
|
||||||
|
AQH_TimeMessage_GetMinute(msg),
|
||||||
|
AQH_TimeMessage_GetSecond(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AQH_TimeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
|
||||||
|
{
|
||||||
|
const char *sCmd;
|
||||||
|
|
||||||
|
switch(AQH_NodeMessage_GetMsgType(msg)) {
|
||||||
|
case AQH_MSG_TYPE_TIME_ANNOUNCE: sCmd="announce"; break;
|
||||||
|
case AQH_MSG_TYPE_TIME_REQSET: sCmd="set"; break;
|
||||||
|
case AQH_MSG_TYPE_TIME_RSPSET: sCmd="ack"; break;
|
||||||
|
default: sCmd="unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GWEN_Buffer_AppendArgs(dbuf,
|
||||||
|
"0x%02x->0x%02x: TIME(%s) %s (uid=0x%08x, msgId=%u, %04d/%02d/%02d-%02d:%02d:%02d (%d))\n",
|
||||||
|
AQH_NodeMessage_GetSourceAddress(msg),
|
||||||
|
AQH_NodeMessage_GetDestAddress(msg),
|
||||||
|
sCmd,
|
||||||
|
sText,
|
||||||
|
(unsigned int) AQH_TimeMessage_GetUid(msg),
|
||||||
|
(unsigned int)AQH_TimeMessage_GetMsgId(msg),
|
||||||
|
AQH_TimeMessage_GetYear(msg),
|
||||||
|
AQH_TimeMessage_GetMonth(msg),
|
||||||
|
AQH_TimeMessage_GetDayOfMonth(msg),
|
||||||
|
AQH_TimeMessage_GetHour(msg),
|
||||||
|
AQH_TimeMessage_GetMinute(msg),
|
||||||
|
AQH_TimeMessage_GetSecond(msg),
|
||||||
|
AQH_TimeMessage_GetDayOfWeek(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
34
aqhome/msg/node/m_time.h
Normal file
34
aqhome/msg/node/m_time.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* This file is part of the project AqHome.
|
||||||
|
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
|
||||||
|
*
|
||||||
|
* The license for this file can be found in the file COPYING which you
|
||||||
|
* should have received along with this file.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef AQH_M_TIME_H
|
||||||
|
#define AQH_M_TIME_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <aqhome/api.h>
|
||||||
|
#include <aqhome/ipc2/message.h>
|
||||||
|
|
||||||
|
#include <gwenhywfar/debug.h>
|
||||||
|
#include <gwenhywfar/timestamp.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AQHOME_API AQH_MESSAGE *AQH_TimeMessage_new(uint8_t destAddr, uint8_t srcAddr, uint8_t code, uint16_t msgId, const GWEN_TIMESTAMP *ts);
|
||||||
|
AQHOME_API uint32_t AQH_TimeMessage_GetUid(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint16_t AQH_TimeMessage_GetMsgId(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint16_t AQH_TimeMessage_GetYear(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetMonth(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetDayOfMonth(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetDayOfWeek(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetHour(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetMinute(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API uint8_t AQH_TimeMessage_GetSecond(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API GWEN_TIMESTAMP *AQH_TimeMessage_GetTime(const AQH_MESSAGE *msg);
|
||||||
|
AQHOME_API void AQH_TimeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
|
||||||
|
|
||||||
|
#endif
|
||||||
13
aqhomed.sh
13
aqhomed.sh
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# AQHOME_LOGLEVEL=info LD_LIBRARY_PATH="../../aqhome/:$LD_LIBRARY_PATH" ./aqhomed -l aqhome.log -db aqhome.db -ma 127.0.0.1
|
|
||||||
|
|
||||||
export AQHOME_LOGLEVEL=info
|
|
||||||
export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH"
|
|
||||||
|
|
||||||
# aqhomed -l /var/log/aqhome.log -db /var/cache/aqhome/nodes.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -W /var/cache/aqhome
|
|
||||||
|
|
||||||
# 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -p aqhomed.pid
|
|
||||||
|
|
||||||
# 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -p aqhomed.pid -W /tmp/aqhome/aqhomed -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 --mqttclientid=AQHOMEMQTTLOGTEST1
|
|
||||||
0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -p aqhomed.pid -t 127.0.0.1 "$@"
|
|
||||||
65
avr/README
Normal file
65
avr/README
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
AqHome Node Operating System (AqOS4Avr)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
This is a very simple operating system for AVR nodes using ATtiny and ATmega MCUs.
|
||||||
|
|
||||||
|
It doesn't use administration objects in RAM, it just defines some routine calls which
|
||||||
|
have to be added into the system to add new drivers and modules.
|
||||||
|
|
||||||
|
Without any modules/drivers included the base system uses:
|
||||||
|
- <500 words of FLASH (<1KB)
|
||||||
|
- 6 bytes of SDRAM
|
||||||
|
|
||||||
|
With full AqHome network stack (including address assignment protocol):
|
||||||
|
- about 1700 words of FLASH
|
||||||
|
- <300 bytes SDRAM (including network buffers)
|
||||||
|
|
||||||
|
|
||||||
|
The following system routine types are defined:
|
||||||
|
|
||||||
|
Function I Description I required I add to file
|
||||||
|
-----------+-------------------------------------------------+----------+----------------------------------
|
||||||
|
INIT I init module/app I yes I modules_init.asm or apps_init.asm
|
||||||
|
RUN I called from the main loop I no I modules_run.asm or apps_run.asm
|
||||||
|
MSGRECVD I called when new messages arrive I no I modules_msg.asm or apps_msg.asm
|
||||||
|
EVERY100MS I called every 100 millisecs (system timer tick) I no I modules_100ms.asm or apps_100ms.asm
|
||||||
|
|
||||||
|
|
||||||
|
If the CLOCK module is enable the following routines are also available:
|
||||||
|
|
||||||
|
Function I Description I required I add to file
|
||||||
|
----------+------------------------------------------------+-----------+----------------------------------
|
||||||
|
EVERY1S I called every second I no I modules_1s.asm or apps_1s.asm
|
||||||
|
EVERY1M I called every minute I no I modules_1m.asm or apps_1m.asm
|
||||||
|
EVERY1H I called every hour I no I modules_1h.asm or apps_1h.asm
|
||||||
|
EVERY1D I called every day I no I modules_1d.asm or apps_1d.asm
|
||||||
|
|
||||||
|
|
||||||
|
Not all modules implement all those functions. Some modules only need to be called
|
||||||
|
once every main loop, others need to be called periodically every once in a while.
|
||||||
|
|
||||||
|
If a module implements a function a call to that routine must be inserted into the appropriate
|
||||||
|
file in "avr/devices/all/".
|
||||||
|
|
||||||
|
In order to make it easier for devices to optionally include modules/apps those calls need to be
|
||||||
|
between preprocessor directives like in:
|
||||||
|
|
||||||
|
----------------------------------X8
|
||||||
|
#ifdef MODULES_DS18B20
|
||||||
|
bigcall Ds18b20_OnEverySecond
|
||||||
|
#endif
|
||||||
|
----------------------------------X8
|
||||||
|
|
||||||
|
|
||||||
|
This way it is very easy to use a module (like that DS18B20 module) by using the
|
||||||
|
following line in your main code:
|
||||||
|
|
||||||
|
----------------------------------X8
|
||||||
|
#define MODULES_DS18B20
|
||||||
|
----------------------------------X8
|
||||||
|
|
||||||
|
This enables all the function calls for this module. Nothing else needs to be changed in your main
|
||||||
|
code file in order to enable that module.
|
||||||
|
|
||||||
|
|
||||||
@@ -107,7 +107,11 @@ appDoorSendValue:
|
|||||||
appDoorSendValue_send:
|
appDoorSendValue_send:
|
||||||
ldi r17, VALUE_ID_TCRT1K ; VALUE ID
|
ldi r17, VALUE_ID_TCRT1K ; VALUE ID
|
||||||
ldi r22, AQHOME_VALUETYPE_DOOR ; VALUE TYPE
|
ldi r22, AQHOME_VALUETYPE_DOOR ; VALUE TYPE
|
||||||
rjmp Main_Send8BitValueReport
|
rcall Main_Send8BitValueReport
|
||||||
|
#ifdef MODULES_LED_ACTIVITY
|
||||||
|
rcall LedActivity_Trigger ; (r16)
|
||||||
|
#endif
|
||||||
|
ret
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -285,12 +285,12 @@ appForwarderLetSysHandleMsg_forMe:
|
|||||||
pop xl
|
pop xl
|
||||||
push xl
|
push xl
|
||||||
push xh
|
push xh
|
||||||
rcall mainModulesOnPacketReceived
|
rcall modulesOnPacketReceived
|
||||||
pop xh
|
pop xh
|
||||||
pop xl
|
pop xl
|
||||||
push xl
|
push xl
|
||||||
push xh
|
push xh
|
||||||
rcall mainAppsOnPacketReceived
|
rcall appsOnPacketReceived
|
||||||
pop xh
|
pop xh
|
||||||
pop xl
|
pop xl
|
||||||
appForwarderLetSysHandleMsg_end:
|
appForwarderLetSysHandleMsg_end:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
App for Motion Activated Light
|
App for Motion Activated Light
|
||||||
==============================
|
==============================
|
||||||
==============================
|
|
||||||
|
|
||||||
This app listens for VALUE_REPORT messages on the bus. If a preselected value from a preselected node
|
This app listens for VALUE_REPORT messages on the bus. If a preselected value from a preselected node
|
||||||
is received (with a value !=0) the LED stripe attached to the node which runs this app will be turned on in
|
is received (with a value !=0) the LED stripe attached to the node which runs this app will be turned on in
|
||||||
@@ -48,7 +47,7 @@ Example:
|
|||||||
1.3. MALSOURCEB
|
1.3. MALSOURCEB
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Source for Birightness Messages:
|
Source for Brightness Messages:
|
||||||
aqhome-tool setdata -N nodes/XXXXXXXX/MALSOURCEB -v VVNN
|
aqhome-tool setdata -N nodes/XXXXXXXX/MALSOURCEB -v VVNN
|
||||||
|
|
||||||
VVNN is a 16-bit value, lower 8 bit contain the source node address, higher 8 bit contain the
|
VVNN is a 16-bit value, lower 8 bit contain the source node address, higher 8 bit contain the
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
|
|
||||||
appMotionLightDataBegin:
|
appMotionLightDataBegin:
|
||||||
appMotionLightTimer: .byte 2
|
|
||||||
appMotionLightOnTime: .byte 2
|
|
||||||
appMotionLightSources: .byte APP_MOTIONLIGHT_SOURCE_NUM*APP_MOTIONLIGHT_SOURCE_SIZE
|
appMotionLightSources: .byte APP_MOTIONLIGHT_SOURCE_NUM*APP_MOTIONLIGHT_SOURCE_SIZE
|
||||||
appMotionLightLSourceAddr: .byte 1
|
appMotionLightLSourceAddr: .byte 1
|
||||||
appMotionLightLSourceValueId: .byte 1
|
appMotionLightLSourceValueId: .byte 1
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ AppMotionLight_Init:
|
|||||||
ldi r17, (appMotionLightDataEnd-appMotionLightDataBegin)
|
ldi r17, (appMotionLightDataEnd-appMotionLightDataBegin)
|
||||||
rcall Utils_FillSram
|
rcall Utils_FillSram
|
||||||
|
|
||||||
ldi r16, LOW(APP_MOTIONLIGHT_DEFAULT_ONTIME)
|
|
||||||
sts appMotionLightOnTime, r16
|
|
||||||
ldi r16, HIGH(APP_MOTIONLIGHT_DEFAULT_ONTIME)
|
|
||||||
sts appMotionLightOnTime+1, r16
|
|
||||||
|
|
||||||
rcall appMotionLightReadConfFromEeprom
|
rcall appMotionLightReadConfFromEeprom
|
||||||
|
|
||||||
ret
|
ret
|
||||||
@@ -44,31 +39,7 @@ AppMotionLight_Init:
|
|||||||
;
|
;
|
||||||
|
|
||||||
AppMotionLight_Fini:
|
AppMotionLight_Fini:
|
||||||
clr r16
|
|
||||||
sts appMotionLightTimer, r16 ; clear timer
|
|
||||||
sts appMotionLightTimer+1, r16
|
|
||||||
rjmp appMotionLightTurnOff
|
|
||||||
; @end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
|
||||||
; @routine AppMotionLight_Every100ms @global
|
|
||||||
;
|
|
||||||
; @clobbers r16, r24, r26, (r17, r18, r19, r20, r21, r23)
|
|
||||||
|
|
||||||
AppMotionLight_Every100ms:
|
|
||||||
lds r24, appMotionLightTimer
|
|
||||||
lds r25, appMotionLightTimer+1
|
|
||||||
sbiw r25:r24, 1
|
|
||||||
brcs AppMotionLight_Every100ms_ret
|
|
||||||
sts appMotionLightTimer, r24
|
|
||||||
sts appMotionLightTimer+1, r25
|
|
||||||
breq AppMotionLight_Every100ms_off
|
|
||||||
AppMotionLight_Every100ms_ret:
|
|
||||||
ret
|
ret
|
||||||
AppMotionLight_Every100ms_off:
|
|
||||||
rjmp appMotionLightTurnOff ; (r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, X)
|
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|
||||||
@@ -129,71 +100,6 @@ appMotionLightIsBrightnessSource_ret:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
|
||||||
; @routine appMotionLightStartTimer
|
|
||||||
;
|
|
||||||
; @clobbers r16
|
|
||||||
|
|
||||||
appMotionLightStartTimer:
|
|
||||||
lds r16, appMotionLightOnTime
|
|
||||||
sts appMotionLightTimer, r16
|
|
||||||
lds r16, appMotionLightOnTime+1
|
|
||||||
sts appMotionLightTimer+1, r16
|
|
||||||
ret
|
|
||||||
; @end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
|
||||||
; @routine appMotionLightTurnOn
|
|
||||||
;
|
|
||||||
; @clobbers r18 (r16, r17, r19, r20, r21, r22, r23, r24, r25, X)
|
|
||||||
|
|
||||||
appMotionLightTurnOn:
|
|
||||||
ldi r18, 1
|
|
||||||
rcall SK6812_SetState ; (R16, R17, R18, R19, R20, R21, R23, R24, R25)
|
|
||||||
ldi r18, 1
|
|
||||||
rjmp appMotionLightReportState ; (R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, X)
|
|
||||||
; @end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
|
||||||
; @routine appMotionLightTurnOff
|
|
||||||
;
|
|
||||||
; @clobbers r18 (r16, r17, r19, r20, r21, r22, r23, r24, r25, X)
|
|
||||||
|
|
||||||
appMotionLightTurnOff:
|
|
||||||
clr r18
|
|
||||||
rcall SK6812_SetState ; (R16, R17, R18, R19, R20, R21, R23, R24, R25)
|
|
||||||
clr r18
|
|
||||||
rjmp appMotionLightReportState ; (R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, X)
|
|
||||||
; @end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
|
||||||
; @routine appMotionLightReportState
|
|
||||||
;
|
|
||||||
; @param R18 state (0=off, 1=on)
|
|
||||||
; @clobbers (R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, X)
|
|
||||||
|
|
||||||
appMotionLightReportState:
|
|
||||||
ldi r17, VALUE_ID_MAL_STATE
|
|
||||||
clr r19
|
|
||||||
ldi r20, 1
|
|
||||||
clr r21
|
|
||||||
ldi r22, AQHOME_VALUETYPE_ONOFF
|
|
||||||
push yl
|
|
||||||
push yh
|
|
||||||
rcall Main_SendValueReport ; (R16, R17, R18, R19, R20, R21, R23, R24, R25, X)
|
|
||||||
pop yh
|
|
||||||
pop yl
|
|
||||||
ret
|
|
||||||
; @end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
; ---------------------------------------------------------------------------
|
||||||
; @routine appMotionLightReadConfFromEeprom
|
; @routine appMotionLightReadConfFromEeprom
|
||||||
;
|
;
|
||||||
@@ -212,8 +118,7 @@ appMotionLightReadConfFromEeprom:
|
|||||||
and r16, r18
|
and r16, r18
|
||||||
cpi r16, 0xff
|
cpi r16, 0xff
|
||||||
breq appMotionLightReadConfFromEeprom_end
|
breq appMotionLightReadConfFromEeprom_end
|
||||||
sts appMotionLightOnTime, r18
|
bigcall SK6812_SetAutoTimerReload ; (none)
|
||||||
sts appMotionLightOnTime+1, r19
|
|
||||||
|
|
||||||
; read source 1
|
; read source 1
|
||||||
rcall Utils_ReadEepromIncr ; (R16)
|
rcall Utils_ReadEepromIncr ; (R16)
|
||||||
@@ -265,10 +170,11 @@ appMotionLightWriteConfToEeprom:
|
|||||||
ldi xl, LOW(EEPROM_OFFS_MAL_CONF_ONTIME)
|
ldi xl, LOW(EEPROM_OFFS_MAL_CONF_ONTIME)
|
||||||
ldi xh, HIGH(EEPROM_OFFS_MAL_CONF_ONTIME)
|
ldi xh, HIGH(EEPROM_OFFS_MAL_CONF_ONTIME)
|
||||||
|
|
||||||
lds r16, appMotionLightOnTime
|
rcall SK6812_GetAutoTimerReload
|
||||||
|
mov r16, r18
|
||||||
rcall Eeprom_WriteByteIfChanged ; (R17)
|
rcall Eeprom_WriteByteIfChanged ; (R17)
|
||||||
adiw xh:xl, 1
|
adiw xh:xl, 1
|
||||||
lds r16, appMotionLightOnTime+1
|
mov r16, r19
|
||||||
rcall Eeprom_WriteByteIfChanged ; (R17)
|
rcall Eeprom_WriteByteIfChanged ; (R17)
|
||||||
adiw xh:xl, 1
|
adiw xh:xl, 1
|
||||||
|
|
||||||
|
|||||||
@@ -21,116 +21,127 @@
|
|||||||
; @clobbers any, -X
|
; @clobbers any, -X
|
||||||
|
|
||||||
AppMotionLight_OnPacketReceived:
|
AppMotionLight_OnPacketReceived:
|
||||||
adiw xh:xl, 2 ; command
|
ldi zl, LOW(appMotionLightMsgTable*2)
|
||||||
ld r16, X
|
ldi zh, HIGH(appMotionLightMsgTable*2)
|
||||||
sbiw xh:xl, 2
|
rjmp Main_HandleValueMsg
|
||||||
cpi r16, NETMSG_CMD_VALUE_SET
|
|
||||||
breq AppMotionLight_OnPacketReceived_set
|
|
||||||
cpi r16, NETMSG_CMD_VALUE_REPORT
|
|
||||||
breq AppMotionLight_OnPacketReceived_report
|
; ---------------------------------------------------------------------------
|
||||||
clc ; unexpected msg
|
; @routine appMotionLightHandleReport
|
||||||
ret
|
;
|
||||||
; "report value" message
|
|
||||||
AppMotionLight_OnPacketReceived_report:
|
appMotionLightHandleReport:
|
||||||
rcall NETMSG_ValueRead ; (none)
|
|
||||||
rcall appMotionLightIsBrightnessSource ; (R16)
|
rcall appMotionLightIsBrightnessSource ; (R16)
|
||||||
brcs AppMotionLight_OnPacketReceived_isBrightness
|
brcs appMotionLightHandleReport_brightness
|
||||||
|
; motion source?
|
||||||
mov r16, r18
|
mov r16, r18
|
||||||
or r16, r19
|
or r16, r19
|
||||||
breq AppMotionLight_OnPacketReceived_clcRet ; zero value, ignore
|
breq appMotionLightHandleReport_ret ; zero value, ignore
|
||||||
rcall appMotionLightHasSource ; (r16, r24, Y)
|
rcall appMotionLightHasSource ; (r16, r24, Y)
|
||||||
brcs AppMotionLight_OnPacketReceived_turnOn
|
brcc appMotionLightHandleReport_ret
|
||||||
ret
|
|
||||||
AppMotionLight_OnPacketReceived_turnOn:
|
; motion detected, check for brightness source
|
||||||
lds r16, appMotionLightLSourceAddr ; do we have a source for brightness value?
|
lds r16, appMotionLightLSourceAddr ; do we have a source for brightness value?
|
||||||
tst r16
|
tst r16
|
||||||
breq AppMotionLight_OnPacketReceived_checkTimer ; no, jmp and don't check brightness
|
breq appMotionLightHandleReport_trigger ; no, jmp and don't check brightness
|
||||||
|
; we have a brightness source configured, do we have a value?
|
||||||
lds r16, appMotionLightFlags ; check last brightness reported
|
lds r16, appMotionLightFlags ; check last brightness reported
|
||||||
andi r16, APP_MOTIONLIGHT_FLAGS_HAVELIGHT
|
andi r16, APP_MOTIONLIGHT_FLAGS_HAVELIGHT
|
||||||
breq AppMotionLight_OnPacketReceived_secRet ; no brightness, yet
|
breq appMotionLightHandleReport_ret ; no brightness, yet
|
||||||
AppMotionLight_OnPacketReceived_checkBrightness:
|
appMotionLightHandleReport_checkBrightness:
|
||||||
lds r16, appMotionLightLSourceValue ; check last brightness reported
|
lds r16, appMotionLightLSourceValue ; check last brightness reported
|
||||||
lds r17, appMotionLightLSourceValue+1
|
lds r17, appMotionLightLSourceValue+1
|
||||||
lds r24, appMotionLightLastBrightness ; r25:r24>r17:r16?
|
lds r24, appMotionLightLastBrightness ; r25:r24>r17:r16?
|
||||||
lds r25, appMotionLightLastBrightness+1 ; appMotionLightLastBrightness>appMotionLightLSourceValue?
|
lds r25, appMotionLightLastBrightness+1 ; appMotionLightLastBrightness>appMotionLightLSourceValue?
|
||||||
sub r16, r24
|
sub r16, r24
|
||||||
sbc r17, r25
|
sbc r17, r25
|
||||||
brcs AppMotionLight_OnPacketReceived_secRet ; yes, too bright to turn on
|
brcs appMotionLightHandleReport_ret ; yes, too bright to turn on
|
||||||
AppMotionLight_OnPacketReceived_checkTimer:
|
appMotionLightHandleReport_trigger:
|
||||||
lds r16, appMotionLightTimer ; time up?
|
bigcall SK6812_Trigger
|
||||||
lds r17, appMotionLightTimer+1
|
rjmp appMotionLightHandleReport_ret
|
||||||
or r16, r17
|
; received report is our brightness source
|
||||||
brne AppMotionLight_OnPacketReceived_startTimer ; no, just restart timer (light already on)
|
appMotionLightHandleReport_brightness:
|
||||||
push xl
|
|
||||||
push xh
|
|
||||||
rcall appMotionLightTurnOn ; (r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, X)
|
|
||||||
pop xh
|
|
||||||
pop xl
|
|
||||||
AppMotionLight_OnPacketReceived_startTimer:
|
|
||||||
rcall appMotionLightStartTimer
|
|
||||||
AppMotionLight_OnPacketReceived_secRet:
|
|
||||||
sec
|
|
||||||
ret
|
|
||||||
AppMotionLight_OnPacketReceived_isBrightness:
|
|
||||||
sts appMotionLightLastBrightness, r18
|
sts appMotionLightLastBrightness, r18
|
||||||
sts appMotionLightLastBrightness+1, r19
|
sts appMotionLightLastBrightness+1, r19
|
||||||
lds r16, appMotionLightFlags
|
lds r16, appMotionLightFlags
|
||||||
ori r16, APP_MOTIONLIGHT_FLAGS_HAVELIGHT
|
ori r16, APP_MOTIONLIGHT_FLAGS_HAVELIGHT
|
||||||
sts appMotionLightFlags, r16
|
sts appMotionLightFlags, r16
|
||||||
sec
|
appMotionLightHandleReport_ret:
|
||||||
|
clc
|
||||||
ret
|
ret
|
||||||
; "set value" message
|
; @end
|
||||||
AppMotionLight_OnPacketReceived_set:
|
|
||||||
rcall NETMSG_ValueRead ; (none)
|
|
||||||
cpi r17, VALUE_ID_MAL_ONTIME
|
|
||||||
breq AppMotionLight_OnPacketReceived_setOnTime
|
; ---------------------------------------------------------------------------
|
||||||
cpi r17, VALUE_ID_MAL_SOURCE1
|
; @routine appMotionLightSetValueOnTime
|
||||||
breq AppMotionLight_OnPacketReceived_setSource1
|
;
|
||||||
cpi r17, VALUE_ID_MAL_SOURCE2
|
|
||||||
breq AppMotionLight_OnPacketReceived_setSource2
|
appMotionLightSetValueOnTime:
|
||||||
cpi r17, VALUE_ID_MAL_BSOURCE
|
rcall SK6812_SetAutoTimerReload
|
||||||
breq AppMotionLight_OnPacketReceived_setBSource
|
rjmp appMotionLightSetValueReturn
|
||||||
cpi r17, VALUE_ID_MAL_BVALUE
|
; @end
|
||||||
breq AppMotionLight_OnPacketReceived_setBValue
|
|
||||||
AppMotionLight_OnPacketReceived_clcRet:
|
|
||||||
clc ; unexpected message
|
; ---------------------------------------------------------------------------
|
||||||
ret
|
; @routine appMotionLightSetValueSource1
|
||||||
AppMotionLight_OnPacketReceived_setBSource: ; setValue "nodes/XXXXXXXX/MALSOURCEB 0xVVNN" (VV=value id, NN=node addr)
|
;
|
||||||
sts appMotionLightLSourceAddr, r18
|
|
||||||
sts appMotionLightLSourceValueId, r19
|
appMotionLightSetValueSource1:
|
||||||
rjmp AppMotionLight_OnPacketReceived_sendAck
|
|
||||||
AppMotionLight_OnPacketReceived_setBValue: ; "setValue nodes/XXXXXXXX/MALVALUEB n" (n brightness value)
|
|
||||||
sts appMotionLightLSourceValue, r18
|
|
||||||
sts appMotionLightLSourceValue+1, r19
|
|
||||||
rjmp AppMotionLight_OnPacketReceived_sendAck
|
|
||||||
AppMotionLight_OnPacketReceived_setOnTime: ; "setValue nodes/XXXXXXXX/MALONTIME n" (n in 1/10 secs)
|
|
||||||
sts appMotionLightOnTime, r18
|
|
||||||
sts appMotionLightOnTime+1, r19
|
|
||||||
lds r16, appMotionLightTimer ; timer active?
|
|
||||||
lds r17, appMotionLightTimer+1
|
|
||||||
or r16, r17
|
|
||||||
breq AppMotionLight_OnPacketReceived_sendAck ; nope, just set it and leave
|
|
||||||
sts appMotionLightTimer, r18 ; restart timer with new value
|
|
||||||
sts appMotionLightTimer+1, r19
|
|
||||||
rjmp AppMotionLight_OnPacketReceived_sendAck
|
|
||||||
AppMotionLight_OnPacketReceived_setSource1: ; setValue "nodes/XXXXXXXX/MALSOURCE1 0xVVNN" (VV=value id, NN=node addr)
|
|
||||||
sts appMotionLightSources, r18 ; peerAddr
|
sts appMotionLightSources, r18 ; peerAddr
|
||||||
sts appMotionLightSources+1, r19 ; valueId
|
sts appMotionLightSources+1, r19 ; valueId
|
||||||
rjmp AppMotionLight_OnPacketReceived_sendAck
|
rjmp appMotionLightSetValueReturn
|
||||||
AppMotionLight_OnPacketReceived_setSource2: ; setValue "nodes/XXXXXXXX/MALSOURCE2 0xVVNN" (VV=value id, NN=node addr)
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; @routine appMotionLightSetValueSource2
|
||||||
|
;
|
||||||
|
|
||||||
|
appMotionLightSetValueSource2:
|
||||||
sts appMotionLightSources+APP_MOTIONLIGHT_SOURCE_SIZE, r18 ; peerAddr
|
sts appMotionLightSources+APP_MOTIONLIGHT_SOURCE_SIZE, r18 ; peerAddr
|
||||||
sts appMotionLightSources+APP_MOTIONLIGHT_SOURCE_SIZE+1, r19 ; valueId
|
sts appMotionLightSources+APP_MOTIONLIGHT_SOURCE_SIZE+1, r19 ; valueId
|
||||||
AppMotionLight_OnPacketReceived_sendAck:
|
rjmp appMotionLightSetValueReturn
|
||||||
push xl
|
; @end
|
||||||
push xh
|
|
||||||
ldi r23, NETMSG_CMD_VALUE_SET_ACK
|
|
||||||
rcall Main_SendValueResponse ; (clobbers all, including Y)
|
|
||||||
rcall appMotionLightWriteConfToEeprom ; (r16, r17, X)
|
; ---------------------------------------------------------------------------
|
||||||
pop xh
|
; @routine appMotionLightSetValueBValue
|
||||||
pop xl
|
;
|
||||||
|
|
||||||
|
appMotionLightSetValueBValue:
|
||||||
|
sts appMotionLightSources, r18 ; peerAddr
|
||||||
|
sts appMotionLightSources+1, r19 ; valueId
|
||||||
|
rjmp appMotionLightSetValueReturn
|
||||||
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; @routine appMotionLightSetValueReturn
|
||||||
|
;
|
||||||
|
; Common return code
|
||||||
|
|
||||||
|
appMotionLightSetValueReturn:
|
||||||
|
rcall appMotionLightWriteConfToEeprom
|
||||||
|
ldi r23, NETMSG_CMD_VALUE_SET_ACK
|
||||||
sec
|
sec
|
||||||
ret
|
ret
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
appMotionLightMsgTable:
|
||||||
|
.db NETMSG_CMD_VALUE_REPORT, 0, LOW(appMotionLightHandleReport), HIGH(appMotionLightHandleReport)
|
||||||
|
.db NETMSG_CMD_VALUE_SET, VALUE_ID_MAL_ONTIME, LOW(appMotionLightSetValueOnTime), HIGH(appMotionLightSetValueOnTime)
|
||||||
|
.db NETMSG_CMD_VALUE_SET, VALUE_ID_MAL_SOURCE1, LOW(appMotionLightSetValueSource1), HIGH(appMotionLightSetValueSource1)
|
||||||
|
.db NETMSG_CMD_VALUE_SET, VALUE_ID_MAL_SOURCE2, LOW(appMotionLightSetValueSource2), HIGH(appMotionLightSetValueSource2)
|
||||||
|
.db NETMSG_CMD_VALUE_SET, VALUE_ID_MAL_BVALUE, LOW(appMotionLightSetValueBValue), HIGH(appMotionLightSetValueBValue)
|
||||||
|
.db 0, 0, 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ appMotionSendValue:
|
|||||||
|
|
||||||
ldi r17, VALUE_ID_MOTION ; VALUE ID
|
ldi r17, VALUE_ID_MOTION ; VALUE ID
|
||||||
ldi r22, AQHOME_VALUETYPE_MOTION ; VALUE TYPE
|
ldi r22, AQHOME_VALUETYPE_MOTION ; VALUE TYPE
|
||||||
rjmp Main_Send8BitValueReport
|
rcall Main_Send8BitValueReport
|
||||||
|
#ifdef MODULES_LED_SIMPLE
|
||||||
|
rcall LedSimple_SignalActivity ; (R18, R19, R20)
|
||||||
|
#endif
|
||||||
|
ret
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ AppNetwork_HandleMsg_handleRebootMsg:
|
|||||||
rcall appNetworkHandleRebootRequest
|
rcall appNetworkHandleRebootRequest
|
||||||
ret
|
ret
|
||||||
AppNetwork_HandleMsg_handlePingMsg:
|
AppNetwork_HandleMsg_handlePingMsg:
|
||||||
|
#ifdef MODULES_LED_SIMPLE
|
||||||
|
bigcall LedSimple_SignalId ; (R18, R19, R20)
|
||||||
|
#endif
|
||||||
rjmp appNetworkHandlePingRequest
|
rjmp appNetworkHandlePingRequest
|
||||||
AppNetwork_HandleMsg_clcRet:
|
AppNetwork_HandleMsg_clcRet:
|
||||||
clc
|
clc
|
||||||
@@ -485,14 +488,9 @@ appNetworkResetState_setRangeEnd:
|
|||||||
; @clobbers X
|
; @clobbers X
|
||||||
|
|
||||||
appNetworkGetAddressFromEeprom:
|
appNetworkGetAddressFromEeprom:
|
||||||
push r15
|
ldi xl, LOW(EEPROM_OFFS_COMADDR)
|
||||||
in r15, SREG
|
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
|
||||||
cli
|
rcall Eeprom_ReadByte ; r16=addr (none)
|
||||||
ldi xl, LOW(EEPROM_OFFS_COMADDR)
|
|
||||||
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
|
|
||||||
bigcall Utils_ReadEepromIncr ; (R16)
|
|
||||||
out SREG, r15
|
|
||||||
pop r15
|
|
||||||
ret
|
ret
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,16 @@ AppReportSensors_OnEverySecond_store:
|
|||||||
breq AppReportSensors_OnEverySecond_sendBrightness
|
breq AppReportSensors_OnEverySecond_sendBrightness
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef APPS_DOOR
|
||||||
|
cpi r16, 100
|
||||||
|
breq AppReportSensors_OnEverySecond_sendDoorValue
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef APPS_MOTION
|
||||||
|
cpi r16, 108
|
||||||
|
breq AppReportSensors_OnEverySecond_sendMotionValue
|
||||||
|
#endif
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
#ifdef MODULES_SI7021
|
#ifdef MODULES_SI7021
|
||||||
@@ -162,6 +172,16 @@ AppReportSensors_OnEverySecond_sendBrightness:
|
|||||||
rjmp Brightness_Send
|
rjmp Brightness_Send
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef APPS_DOOR
|
||||||
|
AppReportSensors_OnEverySecond_sendDoorValue:
|
||||||
|
rjmp appDoorSendValue
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef APPS_MOTION
|
||||||
|
AppReportSensors_OnEverySecond_sendMotionValue:
|
||||||
|
rjmp appMotionSendValue
|
||||||
|
#endif
|
||||||
|
|
||||||
; @end
|
; @end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
tree_t.asm
|
tree_t.asm
|
||||||
eeprom-r.asm
|
eeprom-r.asm
|
||||||
eeprom-w.asm
|
eeprom-w.asm
|
||||||
|
ressource.asm
|
||||||
|
itoa.asm
|
||||||
</extradist>
|
</extradist>
|
||||||
|
|
||||||
</gwbuild>
|
</gwbuild>
|
||||||
|
|||||||
@@ -25,24 +25,47 @@ DEBUG1:
|
|||||||
|
|
||||||
DEBUG2:
|
DEBUG2:
|
||||||
ldi r19, 50
|
ldi r19, 50
|
||||||
ldi r20, 1
|
ldi r20, 5
|
||||||
ldi r21, 1
|
ldi r21, 5
|
||||||
rcall blinkLed
|
rcall blinkLed
|
||||||
rjmp DEBUG2
|
rjmp DEBUG2
|
||||||
|
|
||||||
|
|
||||||
|
DEBUG3:
|
||||||
|
ldi r19, 50
|
||||||
|
ldi r20, 18
|
||||||
|
ldi r21, 2
|
||||||
|
rcall blinkLed
|
||||||
|
rjmp DEBUG3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DEBUG4:
|
||||||
|
ldi r19, 50
|
||||||
|
ldi r20, 18
|
||||||
|
ldi r21, 2
|
||||||
|
rcall blinkLed
|
||||||
|
|
||||||
|
ldi r19, 50
|
||||||
|
ldi r20, 1
|
||||||
|
ldi r21, 9
|
||||||
|
rcall blinkLed
|
||||||
|
|
||||||
|
rjmp DEBUG4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; @param r19 loop count
|
; @param r19 loop count
|
||||||
; @param r20 on time
|
; @param r20 on time
|
||||||
; @param r21 off time
|
; @param r21 off time
|
||||||
; @clobbers (R16, R18, R22, R24, R25)
|
; @clobbers (R19, R22)
|
||||||
|
|
||||||
blinkLed:
|
blinkLed:
|
||||||
sbi LED_SIMPLE_DDR, LED_SIMPLE_PINNUM ; out
|
sbi LED_SIMPLE_DDR, LED_SIMPLE_PINNUM ; out
|
||||||
blinkLed_loop:
|
blinkLed_loop:
|
||||||
cbi LED_SIMPLE_PORT, LED_SIMPLE_PINNUM ; on
|
cbi LED_SIMPLE_PORT, LED_SIMPLE_PINNUM ; on
|
||||||
mov r22, r20
|
mov r22, r20
|
||||||
rcall waitForMultiple100ms ; (R252
|
rcall waitForMultiple100ms ; (R22)
|
||||||
sbi LED_SIMPLE_PORT, LED_SIMPLE_PINNUM ; off
|
sbi LED_SIMPLE_PORT, LED_SIMPLE_PINNUM ; off
|
||||||
mov r22, r21
|
mov r22, r21
|
||||||
rcall waitForMultiple100ms ; (R22)
|
rcall waitForMultiple100ms ; (R22)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user