Compare commits
486 Commits
2024.01.16
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
653efb41a2 | ||
|
|
571ba2f350 | ||
|
|
220cfbf7ae | ||
|
|
db130f646e | ||
|
|
5510c9ff57 | ||
|
|
ebf4e921ee | ||
|
|
d068542c94 | ||
|
|
19fa310f43 | ||
|
|
87772cb76b | ||
|
|
50207a42bf | ||
|
|
a0e6942537 | ||
|
|
c37397acca | ||
|
|
498afe377b | ||
|
|
d43ac7fb92 | ||
|
|
11105944be | ||
|
|
c7fa4ff212 | ||
|
|
96ba58af8c | ||
|
|
d485d1b820 | ||
|
|
8f60a3a12a | ||
|
|
940027ab19 | ||
|
|
24b3f27364 | ||
|
|
5265c6281f | ||
|
|
8acae28c59 | ||
|
|
0061d5e159 | ||
|
|
5d14454185 | ||
|
|
58382be16c | ||
|
|
2edec642fb | ||
|
|
726a08ec2c | ||
|
|
d775ee9e89 | ||
|
|
1c1fcbea51 | ||
|
|
bf89fd7558 | ||
|
|
8247070aae | ||
|
|
241ee1e99d | ||
|
|
a75543c309 | ||
|
|
1c5a3cf6fe | ||
|
|
b2dcac549c | ||
|
|
37b173071e | ||
|
|
041ae7bae7 | ||
|
|
680863fb00 | ||
|
|
8297591853 | ||
|
|
cc3290be8e | ||
|
|
9bfded055a | ||
|
|
2b07e3c2c8 | ||
|
|
eac2e2fb39 | ||
|
|
d843ac6422 | ||
|
|
33a9b7454c | ||
|
|
3dc70ab40a | ||
|
|
ecb5e9cc32 | ||
|
|
9a53d6e209 | ||
|
|
63405a712c | ||
|
|
69c67f96e7 | ||
|
|
d088021902 | ||
|
|
74e3947cb2 | ||
|
|
ca060e406e | ||
|
|
53b496fd00 | ||
|
|
ab60875142 | ||
|
|
3948adf460 | ||
|
|
3c56ec3738 | ||
|
|
661ea6c022 | ||
|
|
3fa864ce52 | ||
|
|
71f312d830 | ||
|
|
f1c095e41d | ||
|
|
bac7179f73 | ||
|
|
9f315207d4 | ||
|
|
08f4d623c7 | ||
|
|
f85297d52f | ||
|
|
e724fb8375 | ||
|
|
54b4a2e9e8 | ||
|
|
94cecc23f5 | ||
|
|
866b539757 | ||
|
|
9132a88963 | ||
|
|
eecd7f7c28 | ||
|
|
ba304b2871 | ||
|
|
0832d3e18c | ||
|
|
3c188f2f9f | ||
|
|
68d2f7bf29 | ||
|
|
ad73fd8abd | ||
|
|
e6a994fd7a | ||
|
|
d324a5c83f | ||
|
|
0aba1595df | ||
|
|
376912d821 | ||
|
|
d3eabc3311 | ||
|
|
d06ea51c7a | ||
|
|
c750defc5f | ||
|
|
130d90ce04 | ||
|
|
55c98ef880 | ||
|
|
6c903abda1 | ||
|
|
28788070b2 | ||
|
|
0fc1ffc4d3 | ||
|
|
8019eaf182 | ||
|
|
225cab676a | ||
|
|
4594bcb23e | ||
|
|
b21e8f8c80 | ||
|
|
8566b08723 | ||
|
|
8452a8d110 | ||
|
|
70f301941b | ||
|
|
d259042542 | ||
|
|
6113e0737b | ||
|
|
b1edb13b3c | ||
|
|
2a21e53422 | ||
|
|
521fce35e4 | ||
|
|
2e23c7e0ae | ||
|
|
68c87c9217 | ||
|
|
4a247f5e94 | ||
|
|
05006e0642 | ||
|
|
d9a8461a2e | ||
|
|
c3d3d947d7 | ||
|
|
e29b86e4dc | ||
|
|
8257eb7aa2 | ||
|
|
1e857b79c1 | ||
|
|
16901482d9 | ||
|
|
aa9f36ee8f | ||
|
|
cf1693e1a0 | ||
|
|
e5cf12cebd | ||
|
|
bcf4b70dc9 | ||
|
|
dc5eb96f50 | ||
|
|
507e86d3b6 | ||
|
|
1900d78122 | ||
|
|
b2522961cd | ||
|
|
499f872641 | ||
|
|
fcf401d20a | ||
|
|
0468ccc34a | ||
|
|
f8ad1acca9 | ||
|
|
36f0ed9ff8 | ||
|
|
ecef32a58e | ||
|
|
9ee947e9b0 | ||
|
|
e533320d92 | ||
|
|
4e293d4b93 | ||
|
|
096a1ba3a0 | ||
|
|
6297ae3428 | ||
|
|
e3b66f7ffe | ||
|
|
da9fb13079 | ||
|
|
b7f830f64e | ||
|
|
90ea73b2ba | ||
|
|
eaa2f07cf3 | ||
|
|
b5ca2cfd21 | ||
|
|
2659204d96 | ||
|
|
6d048ae01d | ||
|
|
d3d96b51ce | ||
|
|
4cd5d79c73 | ||
|
|
2c10e2510b | ||
|
|
8f4b89a193 | ||
|
|
7dac96810f | ||
|
|
10b97fabb4 | ||
|
|
d5abdc6d74 | ||
|
|
edfe06e31e | ||
|
|
d0b2b972e2 | ||
|
|
0c2b6f1a61 | ||
|
|
68793001a2 | ||
|
|
5040636aa2 | ||
|
|
9df3e30bb2 | ||
|
|
38b5807ef7 | ||
|
|
2234ac9703 | ||
|
|
99a37fe01c | ||
|
|
aa5087cc8a | ||
|
|
d5d1a9982f | ||
|
|
ebb225f6c0 | ||
|
|
3a7295c341 | ||
|
|
69d2727106 | ||
|
|
cafdb305a3 | ||
|
|
b05975b97c | ||
|
|
251bb7bd89 | ||
|
|
6f9ded5f20 | ||
|
|
b206cee820 | ||
|
|
759f899620 | ||
|
|
d758a347eb | ||
|
|
0fcf6061c1 | ||
|
|
8b05bd22b5 | ||
|
|
b85e0ab574 | ||
|
|
b43383007a | ||
|
|
d770566aec | ||
|
|
12b9542f72 | ||
|
|
a18e298cdd | ||
|
|
7746d01fc0 | ||
|
|
326525c961 | ||
|
|
355900743d | ||
|
|
818fdc42c9 | ||
|
|
595b153bbf | ||
|
|
cc7145361e | ||
|
|
8db267b21a | ||
|
|
8e26ef4e2e | ||
|
|
67cae68e83 | ||
|
|
468cbad4f3 | ||
|
|
d69a43373e | ||
|
|
155735c828 | ||
|
|
0847f021f1 | ||
|
|
9b565596d5 | ||
|
|
31cf756a7e | ||
|
|
36da830f96 | ||
|
|
5457db269c | ||
|
|
ece4520687 | ||
|
|
1a583e765d | ||
|
|
4364daf54c | ||
|
|
9b9c1e29f1 | ||
|
|
851190dbcc | ||
|
|
992e174bb2 | ||
|
|
ec47e8978f | ||
|
|
a02ad8b52c | ||
|
|
d3903d8602 | ||
|
|
2230850201 | ||
|
|
bb4be0bbf7 | ||
|
|
2fb026074a | ||
|
|
01e43777d2 | ||
|
|
2213ad7bce | ||
|
|
9a318d5170 | ||
|
|
c699f1b487 | ||
|
|
ac5a960581 | ||
|
|
239a77198d | ||
|
|
e5ca0ab784 | ||
|
|
f46a5017c7 | ||
|
|
27910042ea | ||
|
|
d899ea7364 | ||
|
|
7aca72b8fd | ||
|
|
483c10785b | ||
|
|
a7100f238b | ||
|
|
57c5b8c97e | ||
|
|
1c3e7de390 | ||
|
|
96e83f3d37 | ||
|
|
8e68632ed9 | ||
|
|
8de1f7e70f | ||
|
|
bef81eed45 | ||
|
|
181802a76b | ||
|
|
0c012bf62a | ||
|
|
93b6e5a885 | ||
|
|
d6a5fef4e7 | ||
|
|
00584a0787 | ||
|
|
e29ac4f171 | ||
|
|
e37baedddb | ||
|
|
e785904fca | ||
|
|
5c460e26c9 | ||
|
|
a3bd6dd7fb | ||
|
|
c4efda2e0c | ||
|
|
a54b19bf5b | ||
|
|
1115418ce1 | ||
|
|
84e5c0821c | ||
|
|
0c5e702a28 | ||
|
|
a1fddb4ac1 | ||
|
|
fdcbf9de95 | ||
|
|
175e5752fe | ||
|
|
98f4aedbfb | ||
|
|
2f41f43d49 | ||
|
|
3b3e6995c2 | ||
|
|
34e1c43ca7 | ||
|
|
43394bc1bc | ||
|
|
a204263fb2 | ||
|
|
0fec55a659 | ||
|
|
e9b5f3eac7 | ||
|
|
0b59a662df | ||
|
|
304b898ddc | ||
|
|
1fd09d527a | ||
|
|
bd22f00539 | ||
|
|
2f77b9e500 | ||
|
|
ee3b62d671 | ||
|
|
00626b63f7 | ||
|
|
e8b1e7a71c | ||
|
|
d3d92e90e0 | ||
|
|
3e3cf3cd64 | ||
|
|
c2e50a9594 | ||
|
|
5a1d4946fb | ||
|
|
a949776966 | ||
|
|
ac5b6f3097 | ||
|
|
e00d831103 | ||
|
|
8529cb0fca | ||
|
|
7b60c92db9 | ||
|
|
b52cd31309 | ||
|
|
1f3af949a0 | ||
|
|
e4260d3370 | ||
|
|
0cc55f3b87 | ||
|
|
0bb3fc8b94 | ||
|
|
e279cf5cec | ||
|
|
cdaf10a92a | ||
|
|
e85001b2d2 | ||
|
|
eef0335d37 | ||
|
|
931eafff18 | ||
|
|
7627db6c39 | ||
|
|
1984eeeca3 | ||
|
|
03137e15dd | ||
|
|
a17aa031dd | ||
|
|
70dacb5ea6 | ||
|
|
7e52003830 | ||
|
|
342642ec01 | ||
|
|
d8316db20f | ||
|
|
b4e4e3b04d | ||
|
|
45b7f45734 | ||
|
|
4fd0cabe29 | ||
|
|
d09be3a384 | ||
|
|
5ee411fcc6 | ||
|
|
1d92d9ed08 | ||
|
|
1eab3ae773 | ||
|
|
fc1267fe55 | ||
|
|
faed3056dd | ||
|
|
e541a885f5 | ||
|
|
4640ddfba0 | ||
|
|
a667042977 | ||
|
|
ba95f99e03 | ||
|
|
e4bbf55ea5 | ||
|
|
b9b2a19ac5 | ||
|
|
ca4c45fcf2 | ||
|
|
3d66b318ec | ||
|
|
c144b68306 | ||
|
|
119b7b18e6 | ||
|
|
417df65b92 | ||
|
|
a2b568923c | ||
|
|
b5398a4297 | ||
|
|
c960602c62 | ||
|
|
8ef28e27b4 | ||
|
|
d940932d3c | ||
|
|
b2515753c1 | ||
|
|
b1a8f04617 | ||
|
|
ea54397cfc | ||
|
|
7548fceb48 | ||
|
|
cffa7a2b2c | ||
|
|
b27a476507 | ||
|
|
35aa835891 | ||
|
|
8e8c463849 | ||
|
|
6e607f7f67 | ||
|
|
3a4f70dc75 | ||
|
|
5af7e67de7 | ||
|
|
4fea9d81a8 | ||
|
|
72492c267f | ||
|
|
33bfde34b2 | ||
|
|
ea4e7b77f5 | ||
|
|
df80953b5e | ||
|
|
6ce474053e | ||
|
|
e211dd5be2 | ||
|
|
5761e9facf | ||
|
|
24983acf17 | ||
|
|
4972892d9a | ||
|
|
49c2a51980 | ||
|
|
918c3449da | ||
|
|
90711ddd76 | ||
|
|
6d6d62bb77 | ||
|
|
6a7bed0ecf | ||
|
|
6358b1ebee | ||
|
|
a11cc82782 | ||
|
|
1f1227fa10 | ||
|
|
d3b134fe90 | ||
|
|
f6e048b064 | ||
|
|
7d2fb3490e | ||
|
|
d5a24906fa | ||
|
|
c08969b782 | ||
|
|
2cde219317 | ||
|
|
69e257dc8e | ||
|
|
783a7b3868 | ||
|
|
b704126453 | ||
|
|
4623839425 | ||
|
|
5a93a7e4b9 | ||
|
|
5ab4b6d38e | ||
|
|
29403013f5 | ||
|
|
f8cc171e4a | ||
|
|
21cadabd5d | ||
|
|
c36369a83b | ||
|
|
97800434c4 | ||
|
|
d0981934b0 | ||
|
|
68b1a9ee08 | ||
|
|
011f00e5de | ||
|
|
de156ef10a | ||
|
|
b58d08683e | ||
|
|
bf49410f6d | ||
|
|
153293e1c7 | ||
|
|
ea28903761 | ||
|
|
980e847ccb | ||
|
|
2e3125fe8d | ||
|
|
e7a9c96b72 | ||
|
|
aa10c2c5e1 | ||
|
|
b55ca53d1d | ||
|
|
d2d775d687 | ||
|
|
718690030e | ||
|
|
8add226a7c | ||
|
|
58efd9e954 | ||
|
|
6940418955 | ||
|
|
12588655df | ||
|
|
bdff1e1ac3 | ||
|
|
f0a8cabc2c | ||
|
|
1888054627 | ||
|
|
6f3b8fb8e1 | ||
|
|
eff8d52014 | ||
|
|
3b05f447d5 | ||
|
|
326cb15a76 | ||
|
|
bf4d128e49 | ||
|
|
3125f16d99 | ||
|
|
ed326763b7 | ||
|
|
f66b4fa5f1 | ||
|
|
7c60c37f49 | ||
|
|
77b38dff2b | ||
|
|
dc04a63f7c | ||
|
|
cab38d3c84 | ||
|
|
3138e28cdf | ||
|
|
a0d0aec677 | ||
|
|
0b7258d50e | ||
|
|
33bf2117c6 | ||
|
|
437f572c39 | ||
|
|
bd8d93bf92 | ||
|
|
9634c93a3c | ||
|
|
57a997baac | ||
|
|
103207cead | ||
|
|
2526d3dad6 | ||
|
|
f995287a6e | ||
|
|
10cd2e4201 | ||
|
|
b8c1168687 | ||
|
|
3c2b35016a | ||
|
|
021d9b5f44 | ||
|
|
50abcd1061 | ||
|
|
9b7df71da0 | ||
|
|
f80d03210b | ||
|
|
43c3c8d30c | ||
|
|
380879e077 | ||
|
|
bfc604db88 | ||
|
|
06e3498027 | ||
|
|
dfe82ff17e | ||
|
|
03758ad35a | ||
|
|
a1cd1617e4 | ||
|
|
c24a4ea41d | ||
|
|
79e5c642eb | ||
|
|
c4702915a7 | ||
|
|
d22c783a6d | ||
|
|
e8b4e25452 | ||
|
|
c97de9b690 | ||
|
|
d26333dd76 | ||
|
|
1d2055cc89 | ||
|
|
1973cb986c | ||
|
|
d635a9babd | ||
|
|
6036d8efea | ||
|
|
5f51c80022 | ||
|
|
25a66a1722 | ||
|
|
b9bf454237 | ||
|
|
5419135abf | ||
|
|
0fe942adce | ||
|
|
ac91a50ffe | ||
|
|
3cf8fea8e0 | ||
|
|
8e01a275af | ||
|
|
86b9625254 | ||
|
|
0ab5785b7d | ||
|
|
7e2064e264 | ||
|
|
e81a280b87 | ||
|
|
abb37242e8 | ||
|
|
1b637f0870 | ||
|
|
e1564780d6 | ||
|
|
f0b5542c2d | ||
|
|
c27ecc3620 | ||
|
|
557c5d645e | ||
|
|
48a722f826 | ||
|
|
a848275bb9 | ||
|
|
5d7512e026 | ||
|
|
dfed23261a | ||
|
|
e752c433af | ||
|
|
97f006d867 | ||
|
|
2716f4c5df | ||
|
|
21ec72f4c0 | ||
|
|
59022347eb | ||
|
|
2c4952b196 | ||
|
|
ac2e963799 | ||
|
|
c04407f740 | ||
|
|
fe9ac6a2fd | ||
|
|
f0061b976d | ||
|
|
4e669d8932 | ||
|
|
7aece2e143 | ||
|
|
a5cc0a424f | ||
|
|
d99de94c00 | ||
|
|
3f135e8349 | ||
|
|
178525a49b | ||
|
|
4cfb1b1c26 | ||
|
|
4f2fbaaf2a | ||
|
|
8a80289474 | ||
|
|
16fbad92ac | ||
|
|
7bc1a17fac | ||
|
|
251d197fb6 | ||
|
|
e66060e769 | ||
|
|
dcc157261e | ||
|
|
9428eecce4 | ||
|
|
a06d21024c | ||
|
|
6b31a4d470 | ||
|
|
5d63f64411 | ||
|
|
0584eadcf2 | ||
|
|
f7119bc3c7 | ||
|
|
bdfdc3913b | ||
|
|
149444decb | ||
|
|
ce978cb0f5 | ||
|
|
199351c703 | ||
|
|
a7479d33e3 | ||
|
|
41f3955429 | ||
|
|
bfeb852e23 | ||
|
|
72a2c58f1e | ||
|
|
caaa7b6347 | ||
|
|
f26e824247 | ||
|
|
6233ad12ae |
37
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -5,11 +5,18 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: >
|
value: >
|
||||||
### ✋ **This is bug tracker, not a support forum**
|
### ⚠️ Please remember: issues are for *bugs*
|
||||||
|
That is, something you believe affects every single user of OpenDTU, not just you. If you're not sure, start with one of the other options below.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
#### Have a question? 👉 [Start a new discussion](https://github.com/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB).
|
||||||
|
|
||||||
If something isn't working right, you have questions or need help, [**get in touch on the Discussions**](https://github.com/tbnobody/OpenDTU/discussions).
|
#### Before opening an issue, please double check:
|
||||||
|
|
||||||
Please quickly search existing issues first before submitting a bug.
|
- [Documentation](https://www.opendtu.solar).
|
||||||
|
- [The FAQs](https://www.opendtu.solar/firmware/faq/).
|
||||||
|
- [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues).
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: what-happened
|
id: what-happened
|
||||||
attributes:
|
attributes:
|
||||||
@ -40,7 +47,8 @@ body:
|
|||||||
label: Install Method
|
label: Install Method
|
||||||
description: How did you install OpenDTU?
|
description: How did you install OpenDTU?
|
||||||
options:
|
options:
|
||||||
- Pre-Compiled binary from GitHub
|
- Pre-Compiled binary from GitHub releases
|
||||||
|
- Pre-Compiled binary from GitHub actions/pull-request
|
||||||
- Self-Compiled
|
- Self-Compiled
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -52,6 +60,14 @@ body:
|
|||||||
placeholder: "e.g. 359d513"
|
placeholder: "e.g. 359d513"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: What firmware variant (PIO Environment) are you using?
|
||||||
|
description: You can find this in by going to Info -> System
|
||||||
|
placeholder: "generic_esp32s3_usb"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: logs
|
id: logs
|
||||||
attributes:
|
attributes:
|
||||||
@ -66,3 +82,16 @@ body:
|
|||||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: required-checks
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following
|
||||||
|
options:
|
||||||
|
- label: I believe this issue is a bug that affects all users of OpenDTU, not something specific to my installation.
|
||||||
|
required: true
|
||||||
|
- label: I have already searched for relevant existing issues and discussions before opening this report.
|
||||||
|
required: true
|
||||||
|
- label: I have updated the title field above with a concise description.
|
||||||
|
required: true
|
||||||
|
- label: I have double checked that my inverter does not contain a W in the model name (like HMS-xxxW) as they are not supported.
|
||||||
|
required: true
|
||||||
|
|||||||
71
.github/workflows/build.yml
vendored
@ -1,15 +1,10 @@
|
|||||||
name: OpenDTU-onBattery Build
|
name: OpenDTU Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- docs/**
|
- docs/**
|
||||||
- '**/*.md'
|
- '**/*.md'
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- development
|
|
||||||
tags-ignore:
|
|
||||||
- v*
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- docs/**
|
- docs/**
|
||||||
@ -23,14 +18,14 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -48,7 +43,7 @@ jobs:
|
|||||||
environments: ${{ steps.envs.outputs.environments }}
|
environments: ${{ steps.envs.outputs.environments }}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Enviornments
|
name: Build Environments
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: get_default_envs
|
needs: get_default_envs
|
||||||
strategy:
|
strategy:
|
||||||
@ -61,7 +56,7 @@ jobs:
|
|||||||
run: git fetch --force --tags origin
|
run: git fetch --force --tags origin
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
@ -69,13 +64,13 @@ jobs:
|
|||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- name: Cache PlatformIO
|
- name: Cache PlatformIO
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -84,64 +79,56 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade platformio setuptools
|
pip install --upgrade platformio setuptools
|
||||||
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: |
|
||||||
|
cd webapp
|
||||||
|
corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build WebApp
|
- name: Build WebApp
|
||||||
run: yarn --cwd webapp build
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn build
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: pio run -e ${{ matrix.environment }}
|
run: pio run -e ${{ matrix.environment }}
|
||||||
|
|
||||||
- name: Rename Firmware
|
- name: Rename Firmware
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
|
||||||
|
|
||||||
- name: Rename Factory Firmware
|
- name: Rename Factory Firmware
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: opendtu-onbattery-${{ matrix.environment }}
|
name: opendtu-${{ matrix.environment }}
|
||||||
path: |
|
path: |
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [get_default_envs, build]
|
needs: [get_default_envs, build]
|
||||||
if: startsWith(github.ref, 'refs/tags/2')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get tags
|
|
||||||
run: git fetch --force --tags origin
|
|
||||||
|
|
||||||
- name: Get openDTU core release
|
|
||||||
run: |
|
|
||||||
echo "OPEN_DTU_CORE_RELEASE=$(git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags | grep 'refs/tags/v' | tail -1 | sed 's#.*/##' | sed 's/ .*//')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Create openDTU-core-release-Badge
|
|
||||||
uses: schneegans/dynamic-badges-action@v1.6.0
|
|
||||||
with:
|
|
||||||
auth: ${{ secrets.GIST_SECRET }}
|
|
||||||
gistID: 68b47cc8c8994d04ab3a4fa9d8aee5e6
|
|
||||||
filename: openDTUcoreRelease.json
|
|
||||||
label: based on original OpenDTU
|
|
||||||
message: ${{ env.OPEN_DTU_CORE_RELEASE }}
|
|
||||||
color: lightblue
|
|
||||||
|
|
||||||
- name: Build Changelog
|
- name: Build Changelog
|
||||||
id: github_release
|
id: github_release
|
||||||
uses: mikepenz/release-changelog-builder-action@v3
|
uses: mikepenz/release-changelog-builder-action@v4
|
||||||
with:
|
with:
|
||||||
failOnError: true
|
failOnError: true
|
||||||
commitMode: true
|
commitMode: true
|
||||||
@ -157,10 +144,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
ls -R
|
ls -R
|
||||||
cd artifacts
|
cd artifacts
|
||||||
for i in */; do cp ${i}opendtu-onbattery-*.bin ./; done
|
for i in */; do cp ${i}opendtu-*.bin ./; done
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
body: ${{steps.github_release.outputs.changelog}}
|
body: ${{steps.github_release.outputs.changelog}}
|
||||||
draft: False
|
draft: False
|
||||||
|
|||||||
@ -18,6 +18,12 @@
|
|||||||
"fix"
|
"fix"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "## 🌎 Web Application",
|
||||||
|
"labels": [
|
||||||
|
"webapp"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "## 📚 Documentation",
|
"title": "## 📚 Documentation",
|
||||||
"labels": [
|
"labels": [
|
||||||
@ -30,7 +36,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"template": "${{CHANGELOG}}",
|
"template": "${{CHANGELOG}}",
|
||||||
"pr_template": "- [${{TITLE}}](https://github.com/helgeerbe/OpenDTU-OnBattery/commit/${{MERGE_SHA}})",
|
"pr_template": "- [${{TITLE}}](https://github.com/tbnobody/OpenDTU/commit/${{MERGE_SHA}})",
|
||||||
"empty_template": "- no changes",
|
"empty_template": "- no changes",
|
||||||
"label_extractor": [
|
"label_extractor": [
|
||||||
{
|
{
|
||||||
|
|||||||
4
.github/workflows/cpplint.yml
vendored
@ -7,9 +7,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
54
.github/workflows/repo-maintenance.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
name: 'Repository Maintenance'
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 4 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
discussions: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
name: 'Stale'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
days-before-stale: 14
|
||||||
|
days-before-close: 60
|
||||||
|
any-of-labels: 'cant-reproduce,not a bug'
|
||||||
|
stale-issue-label: stale
|
||||||
|
stale-pr-label: stale
|
||||||
|
stale-issue-message: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
|
||||||
|
lock-threads:
|
||||||
|
name: 'Lock Old Threads'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v5
|
||||||
|
with:
|
||||||
|
issue-inactive-days: '30'
|
||||||
|
pr-inactive-days: '30'
|
||||||
|
discussion-inactive-days: '30'
|
||||||
|
log-output: true
|
||||||
|
issue-comment: >
|
||||||
|
This issue has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
pr-comment: >
|
||||||
|
This pull request has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
discussion-comment: >
|
||||||
|
This discussion has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion for related concerns.
|
||||||
103
.github/workflows/test_build.yml
vendored
@ -1,103 +0,0 @@
|
|||||||
name: OpenDTU-onBattery Test Build
|
|
||||||
|
|
||||||
on: workflow_dispatch
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
get_default_envs:
|
|
||||||
name: Gather Environments
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Cache pip
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pip
|
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pip-
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.9"
|
|
||||||
|
|
||||||
- name: Install PlatformIO
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install --upgrade platformio
|
|
||||||
|
|
||||||
- name: Get default environments
|
|
||||||
id: envs
|
|
||||||
run: |
|
|
||||||
echo "environments=$(pio project config --json-output | jq -cr '.[1][1][0][1]|split(",")')" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
environments: ${{ steps.envs.outputs.environments }}
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build Enviornments
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: get_default_envs
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Get tags
|
|
||||||
run: git fetch --force --tags origin
|
|
||||||
|
|
||||||
- name: Cache pip
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pip
|
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pip-
|
|
||||||
|
|
||||||
- name: Cache PlatformIO
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ~/.platformio
|
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.9"
|
|
||||||
|
|
||||||
- name: Install PlatformIO
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install --upgrade platformio
|
|
||||||
|
|
||||||
- name: Setup Node.js and yarn
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: "18"
|
|
||||||
cache: "yarn"
|
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build WebApp
|
|
||||||
run: yarn --cwd webapp build
|
|
||||||
|
|
||||||
- name: Build firmware
|
|
||||||
run: pio run -e ${{ matrix.environment }}
|
|
||||||
|
|
||||||
- name: Rename Firmware
|
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
|
||||||
|
|
||||||
- name: Rename Factory Firmware
|
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: opendtu-onbattery-${{ matrix.environment }}
|
|
||||||
path: |
|
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
|
||||||
|
|
||||||
|
|
||||||
16
.github/workflows/yarnlint.yml
vendored
@ -6,17 +6,23 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "18"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Linting
|
- name: Linting
|
||||||
run: yarn --cwd webapp lint
|
run: yarn lint
|
||||||
|
|||||||
28
.github/workflows/yarnprettier.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Yarn Prettier
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
- name: Setup Node.js and yarn
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install WebApp dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check Formatting
|
||||||
|
run: yarn prettier --check src/
|
||||||
1
.vscode/extensions.json
vendored
@ -5,7 +5,6 @@
|
|||||||
"DavidAnson.vscode-markdownlint",
|
"DavidAnson.vscode-markdownlint",
|
||||||
"EditorConfig.EditorConfig",
|
"EditorConfig.EditorConfig",
|
||||||
"Vue.volar",
|
"Vue.volar",
|
||||||
"Vue.vscode-typescript-vue-plugin",
|
|
||||||
"platformio.platformio-ide"
|
"platformio.platformio-ide"
|
||||||
],
|
],
|
||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
|
|||||||
62
.vscode/settings.json
vendored
@ -1,63 +1,3 @@
|
|||||||
{
|
{
|
||||||
"C_Cpp.clang_format_style": "WebKit",
|
"C_Cpp.clang_format_style": "WebKit"
|
||||||
"files.associations": {
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"chrono": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"condition_variable": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"list": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"regex": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iomanip": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"mutex": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"variant": "cpp"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
77
README.md
@ -1,60 +1,43 @@
|
|||||||
- [OpenDTU-onBattery](#opendtu-onbattery)
|
# OpenDTU
|
||||||
- [What is OpenDTU-onBattery](#what-is-opendtu-onbattery)
|
|
||||||
- [History of the project](#history-of-the-project)
|
|
||||||
- [Highlights of OpenDTU-onBattery](#highlights-of-opendtu-onbattery)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [Acknowledgment](#acknowledgment)
|
|
||||||
|
|
||||||
# OpenDTU-onBattery
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml)
|
||||||
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml)
|
||||||
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml)
|
||||||
|
|
||||||
This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/OpenDTU).
|
## !! IMPORTANT UPGRADE NOTES !!
|
||||||
|
|
||||||

|
If you are upgrading from a version before 15.03.2023 you have to upgrade the partition table of the ESP32. Please follow the [this](docs/UpgradePartition.md) documentation!
|
||||||
|
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
## Background
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
|
||||||
|
|
||||||
## What is OpenDTU-onBattery
|
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
|
||||||
|
It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
|
||||||
OpenDTU-onBattery is an extension of the original OpenDTU to support battery chargers, battery management systems (BMS) and power meters on a single esp32. With the help of a dynamic power limiter, the power production can be adjusted to the actual consumption. In this way, it is possible to come as close as possible to the goal of zero feed-in.
|
|
||||||
|
|
||||||
## History of the project
|
|
||||||
|
|
||||||
The original OpenDTU project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net). It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
|
|
||||||
|
|
||||||
Summer 2022 I bought my Victron MPPT battery charger, and didn't like the idea to set up a separate esp32 to recieve the charger data. I decided to fork OpenDTU and extend it with battery charger support and a dynamic power limitter to my own needs. Hoping someone can make use of it.
|
|
||||||
|
|
||||||
## Highlights of OpenDTU-onBattery
|
|
||||||
|
|
||||||
This project is still under development and adds following features:
|
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
>
|
|
||||||
> In contrast to the original openDTU, with release 2023.05.23.post1 openDTU-onBattery supports only 5 inverters. Otherwise, there is not enough memory for the liveData view.
|
|
||||||
|
|
||||||
* Support Victron's Ve.Direct protocol on the same chip (cable based serial interface!). Additional information about Ve.direct can be downloaded directly from [Victron's website](https://www.victronenergy.com/support-and-downloads/technical-information).
|
|
||||||
* Dynamically sets the Hoymiles power limited according to the currently used energy in the household. Needs an HTTP JSON based power meter (e.g. Tasmota), an MQTT based power meter like Shelly 3EM or an SDM power meter.
|
|
||||||
* Battery support: Read the voltage from Victron MPPT charge controller or from the Hoymiles DC inputs and starts/stops the power producing based on configurable voltage thresholds
|
|
||||||
* Voltage correction that takes the voltage drop because of the current output load into account (not 100% reliable calculation)
|
|
||||||
* Can read the current solar panel power from the Victron MPPT and adjust the limiter accordingly to not save energy in the battery (for increased system efficiency). Increases the battery lifespan and reduces energy loses.
|
|
||||||
* Settings can be configured in the UI
|
|
||||||
* Pylontech Battery support (via CAN bus interface). Use the SOC for starting/stopping the power output and provide the battery data via MQTT (autodiscovery for home assistant is currently not supported). Pin Mapping is supported (default RX PIN 27, TX PIN 26). Actual no live view support for Pylontech Battery.
|
|
||||||
* Huawei R4850G2 power supply unit that can act as AC charger. Supports status shown on the web interface and options to set voltage and current limits on the web interface and via MQTT. Connection is done using CAN bus (needs to be separate from Pylontech CAN bus) via SN65HVD230 interface.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[Full documentation of OpenDTU-onBattery extensions can be found at the project's wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki).
|
The documentation can be found [here](https://tbnobody.github.io/OpenDTU-docs/).
|
||||||
|
Please feel free to support and create a PR in [this](https://github.com/tbnobody/OpenDTU-docs) repository to make the documentation even better.
|
||||||
|
|
||||||
For documentation of openDTU core functionality I refer to the original [repo](https://github.com/tbnobody/OpenDTU) and its [wiki](https://github.com/tbnobody/OpenDTU/wiki).
|
## Breaking changes
|
||||||
|
|
||||||
Please note that openDTU-onBattery may change significantly during its development.
|
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
||||||
Bug reports, comments, feature requests and fixes are most welcome!
|
|
||||||
|
|
||||||
To find out what's new or improved have a look at the [changelog](https://github.com/helgeerbe/OpenDTU-OnBattery/releases).
|
```code
|
||||||
|
* 1b637f08 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* e1564780 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* f0b5542c 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* c27ecc36 2024-01-29 BREAKING CHANGE: Web API Endpoint /api/livedata/status
|
||||||
|
* 71d1b3b 2023-11-07 BREAKING CHANGE: Home Assistant Auto Discovery to new naming scheme
|
||||||
|
* 04f62e0 2023-04-20 BREAKING CHANGE: Web API Endpoint /api/eventlog/status no nested serial object
|
||||||
|
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
|
||||||
|
* 318136d 2023-03-15 BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
|
||||||
|
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
|
||||||
|
* d4c838a 2023-02-06 BREAKING CHANGE: Prometheus API!
|
||||||
|
* daf847e 2022-11-14 BREAKING CHANGE: Removed deprecated config parsing method
|
||||||
|
* 69b675b 2022-11-01 BREAKING CHANGE: Structure WebAPI /api/livedata/status changed
|
||||||
|
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
|
||||||
|
```
|
||||||
|
|
||||||
## Acknowledgment
|
## Currently supported Inverters
|
||||||
|
|
||||||
A special Thank to Thomas Basler (tbnobody) the author of the original [OpenDTU](https://github.com/tbnobody/OpenDTU) project. You are doing a great job!
|
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||||
|
|
||||||
Last but not least, I would like to thank all the contributors. With your ideas and enhancements, you have made OpenDTU-onBattery much more than I originally had in mind.
|
|
||||||
|
|||||||
@ -1,186 +0,0 @@
|
|||||||
# OpenDTU-OnBattery
|
|
||||||
|
|
||||||
This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/OpenDTU). This project is still under development but is being used on a day to day basis as well.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
>
|
|
||||||
> In contrast to the original openDTU, with release 2023.05.23.post1 openDTU-onBattery supports only 5 inverters. Otherwise, there is not enough memory for the liveData view.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Hoymiles inverter support for live data and display of various inverter internal information. (Partial) support for multiple inverters.
|
|
||||||
* MQTT support (with TLS) with partial Home Assistant MQTT Auto Discovery
|
|
||||||
* Automatic inverter power control of a selected Hoymiles inverter to compensate the currently used energy in the household.
|
|
||||||
* Energy meter support with interface options to HTTP JSON based power meters (e.g. Tasmota), MQTT based power meters (e.g. Shelly 3EM) or SDM power meters.
|
|
||||||
* Support for Victron MPPT charge controller using Ve.Direct. cf. Ve.direct: https://www.victronenergy.com/support-and-downloads/technical-information.
|
|
||||||
* Generic voltage based battery support using Victron MPPT charge controller or Hoymiles inverter voltage values to start / stop inverter power output. (with load compensation)
|
|
||||||
* Pylontech battery support via CAN bus interface. State of Charge reported by BMS is used to start / stop inverter power output. Battery data is exported via MQTT (no support for home assistant auto discovery).
|
|
||||||
* Support for Huawei R4850G2 power supply unit that can act as AC charging source. [Overview](https://www.beyondlogic.org/review-huawei-r4850g2-power-supply-53-5vdc-3kw/)
|
|
||||||
* Other features from [OpenDTU](https://github.com/tbnobody/OpenDTU) maintained
|
|
||||||
|
|
||||||
## Hardware
|
|
||||||
|
|
||||||
To get started with this project you will need to assemble a few hardware components that allow interfacing with the desired devices. What is needed depends on the use-case but may consist of:
|
|
||||||
|
|
||||||
* ESP32 board that contains the CPU and WIFI connectivity
|
|
||||||
* NRF24L01+ or CMT2300A radio board to interface with the inverter. Please check the list of the supported inverters below for the board needed.
|
|
||||||
* 3.3V / 5V logic level shifter to interface with the Victron MPPT charge controller
|
|
||||||
* SN65HVD230 CAN bus transceiver to interface with a Pylontech battery
|
|
||||||
* MCP2515 SPI / CAN bus transceiver to interface with the Huawei AC PSU
|
|
||||||
* Relais board + 3.3V / 5 V logic level shifter to switch the slot detect on the Huawei AC PSU
|
|
||||||
* Display [Display](docs/Display.md)
|
|
||||||
|
|
||||||
More detailed information on the hardware can be found in the [Hardware and flashing](docs/hardware_flash.md) document.
|
|
||||||
|
|
||||||
### Currently supported Inverters
|
|
||||||
|
|
||||||
| Model | Required RF Module | DC Inputs | MPP-Tracker | AC Phases |
|
|
||||||
| --------------------| ------------------ | --------- | ----------- | --------- |
|
|
||||||
| Hoymiles HM-300 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-400 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-600 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-700 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1000 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1200 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1500 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-300 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-350 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-400 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-450 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-500 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-600 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-700 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-800 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-900 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1000 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1600 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-1800 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-2000 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMT-1800 | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Hoymiles HMT-2250 | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Solenso SOL-H350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H400 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| TSUN TSOL-M800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M1600 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
|
|
||||||
**TSUN compatibility remark:**
|
|
||||||
Compatibility with OpenDTU is most likely related to the serial number of the inverter. Current findings indicate that TSUN inverters with a serial number starting with "11" are supported, whereby inverters with a serial number starting with "10" are not.
|
|
||||||
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
Several screenshots of the frontend can be found here: [Screenshots](docs/screenshots/README.md)
|
|
||||||
|
|
||||||
## Configuration and usage
|
|
||||||
|
|
||||||
### First configuration
|
|
||||||
|
|
||||||
* After the [initial flashing](docs/hardware_flash.md#flashing-and-starting-up) of the microcontroller, an Access Point called "OpenDTU-*" is opened. The default password is "openDTU42".
|
|
||||||
* Use a web browser to open the address [http://192.168.4.1](http://192.168.4.1)
|
|
||||||
* Navigate to Settings --> Network Settings and enter your WiFi credentials. The username to access the config menu is "admin" and the password the same as for accessing the Access Point (default: "openDTU42").
|
|
||||||
* OpenDTU then simultaneously connects to your WiFi AP with these credentials. Navigate to Info --> Network and look into section "Network Interface (Station)" for the IP address received via DHCP.
|
|
||||||
* If your WiFi AP uses an allow-list for MAC-addresses, please be aware that the ESP32 has two different MAC addresses for its AP and client modes, they are also listed at Info --> Network.
|
|
||||||
* When OpenDTU is connected to a configured WiFI AP, the "OpenDTU-*" Access Point is closed after 3 minutes.
|
|
||||||
* OpenDTU needs access to a working NTP server to get the current date & time. Both are sent to the inverter with each request. Default NTP server is pool.ntp.org. If your network has different requirements please change accordingly (Settings --> NTP Settings).
|
|
||||||
* Activate Ve.direct, Battery and the AC Charger according to the available hardware
|
|
||||||
* Configure a Power Meter to provide a data source for the current consumption
|
|
||||||
* Configure the Dynamic Power Limiter according to the used battery. Documentation about the power limiter interface and states can be found below.
|
|
||||||
* If desired connect to a home automation system using MQTT or the Webapi.
|
|
||||||
* A documentation of all available MQTT Topics can be found here: [MQTT Documentation](docs/MQTT_Topics.md)
|
|
||||||
* A documentation of the Web API can be found here: [Web-API Documentation](docs/Web-API.md)
|
|
||||||
* Home Assistant auto discovery is supported. [Example image](https://user-images.githubusercontent.com/59169507/217558862-a83846c5-6070-43cd-9a0b-90a8b2e2e8c6.png)
|
|
||||||
|
|
||||||
### Huawei PSU
|
|
||||||
|
|
||||||
The Huawei PSU can be used to charge a battery. This can be be useful if an external (AC) connected solar system shall be utilized or if variable energy prices should be exploited.
|
|
||||||
|
|
||||||
Some points for consideration are:
|
|
||||||
* Make sure to consider the PSU voltage range when selecting the battery voltage as lower voltages <42V are not supported.
|
|
||||||
* The PSU runs a noisy fan and it is therefore desireable to switch it off when not being used. Some users have found that switching the slot detect pins with a relay accomplishes this. A GPIO pin is made available from the ESP to turn the PSU on/off
|
|
||||||
|
|
||||||
#### Operation modes
|
|
||||||
|
|
||||||
openDTU-onBattery supports three operation modes for the Huawei PSU:
|
|
||||||
1. Fully manual - In this mode the PSU needs to be turned on/off externally using MQTT and voltage and current limits need to be provided. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands
|
|
||||||
2. Manual with auto power on / off - In this mode the PSU is turned on when a current limit > 1A is set. If the current limit is < 1A for some time the PSU is turned off. Current and voltage limits need to be provided externally using MQTT. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands.
|
|
||||||
3. Automatic - In this mode the PSU power is controlled by the Power Meter and information provided in the web-interface. If excess power is present the PSU is turned on. The voltage limit is set as per web-interface and the current limit is set so that the maximum PSU output power equals the Power Meter value. Minium and maximum PSU power levels as configured in the web-interface are respected in this process. The PSU is turned off if the output current is limited and the output power drops below the minium power level. This will disable automatic mode until the battery is discharged below the start voltage level (set in the web-interface). This mode can be enabled using the web-interface and MQTT. See [MQTT Documentation](docs/MQTT_Topics.md)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
* First: When there is no light on the solar panels, the inverter completely turns off and does not answer to OpenDTU! So if you assembled your OpenDTU in the evening, wait until tomorrow.
|
|
||||||
* When there is no data received from the inverter(s) - try to reduce the distance between the openDTU and the inverter (e.g. move it to the window towards the roof)
|
|
||||||
* Under Settings -> DTU Settings you can increase the transmit power "PA level". Default is "minimum".
|
|
||||||
* The NRF24L01+ needs relatively much current. With bad power supply (and especially bad cables!) a 10 µF capacitor soldered directly to the NRF24L01+ board connector brings more stability (pin 1+2 are the power supply). Note the polarity of the capacitor…
|
|
||||||
* You can try to use an USB power supply with 1 A or more instead of connecting the ESP32 to the computer.
|
|
||||||
* Try a different USB cable. Once again, a stable power source is important. Some USB cables are made of much plastic and very little copper inside.
|
|
||||||
* Double check that you have a radio module NRF24L01+ with a plus sign at the end. NRF24L01 module without the plus are not compatible with this project.
|
|
||||||
* There is no possibility of auto-discovering the inverters. Double check you have entered the serial numbers of the inverters correctly.
|
|
||||||
* OpenDTU needs access to a working NTP server to get the current date & time.
|
|
||||||
* If your problem persists, check the [Issues on Github](https://github.com/tbnobody/OpenDTU/issues). Please inspect not only the open issues, also the closed issues contain useful information.
|
|
||||||
* Another source of information are the [Discussions](https://github.com/tbnobody/OpenDTU/discussions/)
|
|
||||||
* When flashing with VSCode Plattform.IO fails and also with ESPRESSIF tool a demo bin file cannot be flashed to the ESP32 with error message "A fatal error occurred: MD5 of file does not match data in flash!" than un-wire/unconnect ESP32 from the NRF24L01+ board. Try to flash again and rewire afterwards.
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
|
|
||||||
It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
|
|
||||||
|
|
||||||
## Features for developers
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
|
||||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
|
||||||
|
|
||||||
### Core technologies used
|
|
||||||
|
|
||||||
* The microcontroller part
|
|
||||||
* Build with Arduino PlatformIO Framework for the ESP32
|
|
||||||
* Uses a fork of [ESPAsyncWebserver](https://github.com/yubox-node-org/ESPAsyncWebServer) and [espMqttClient](https://github.com/bertmelis/espMqttClient)
|
|
||||||
|
|
||||||
* The WebApp part
|
|
||||||
* Build with [Vue.js](https://vuejs.org)
|
|
||||||
* Source is written in TypeScript
|
|
||||||
|
|
||||||
### Breaking changes
|
|
||||||
|
|
||||||
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
|
||||||
|
|
||||||
```code
|
|
||||||
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
|
|
||||||
* 318136d 2023-03-15 BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
|
|
||||||
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
|
|
||||||
* d4c838a 2023-02-06 BREAKING CHANGE: Prometheus API!
|
|
||||||
* daf847e 2022-11-14 BREAKING CHANGE: Removed deprecated config parsing method
|
|
||||||
* 69b675b 2022-11-01 BREAKING CHANGE: Structure WebAPI /api/livedata/status changed
|
|
||||||
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
* Building the WebApp
|
|
||||||
* The WebApp can be build using yarn
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd webapp
|
|
||||||
yarn install
|
|
||||||
yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
* The updated output is placed in the 'webapp_dist' directory
|
|
||||||
* It is only necessary to build the webapp when you made changes to it
|
|
||||||
* Building the microcontroller firmware
|
|
||||||
* Visual Studio Code with the PlatformIO Extension is required for building
|
|
||||||
|
|
||||||
## Related Projects
|
|
||||||
|
|
||||||
* [Ahoy](https://github.com/grindylow/ahoy)
|
|
||||||
* [DTU Simulator](https://github.com/Ziyatoe/DTUsimMI1x00-Hoymiles)
|
|
||||||
* [OpenDTU extended to talk to Victrons MPPT battery chargers (Ve.Direct)](https://github.com/helgeerbe/OpenDTU_VeDirect)
|
|
||||||
@ -1,91 +1,3 @@
|
|||||||
# Device Profiles
|
# Device Profiles
|
||||||
|
|
||||||
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/>
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/>
|
||||||
|
|
||||||
## Structure of the json file for openDTU-onBattery (outdated example)
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Generic NodeMCU 38 pin",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 19,
|
|
||||||
"mosi": 23,
|
|
||||||
"clk": 18,
|
|
||||||
"irq": 16,
|
|
||||||
"en": 4,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"victron": {
|
|
||||||
"rx": 22,
|
|
||||||
"tx": 21
|
|
||||||
},
|
|
||||||
"battery": {
|
|
||||||
"rx": 27,
|
|
||||||
"tx": 14
|
|
||||||
},
|
|
||||||
"huawei": {
|
|
||||||
"miso": 12,
|
|
||||||
"mosi": 13,
|
|
||||||
"clk": 26,
|
|
||||||
"irq": 25,
|
|
||||||
"power": 33,
|
|
||||||
"cs": 15
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": false,
|
|
||||||
"phy_addr": -1,
|
|
||||||
"power": -1,
|
|
||||||
"mdc": -1,
|
|
||||||
"mdio": -1,
|
|
||||||
"type": -1,
|
|
||||||
"clk_mode": -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Generic NodeMCU 38 pin with SSD1306",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 19,
|
|
||||||
"mosi": 23,
|
|
||||||
"clk": 18,
|
|
||||||
"irq": 16,
|
|
||||||
"en": 4,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": false,
|
|
||||||
"phy_addr": -1,
|
|
||||||
"power": -1,
|
|
||||||
"mdc": -1,
|
|
||||||
"mdio": -1,
|
|
||||||
"type": -1,
|
|
||||||
"clk_mode": -1
|
|
||||||
},
|
|
||||||
"display": {
|
|
||||||
"type": 2,
|
|
||||||
"data": 21,
|
|
||||||
"clk": 22
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Olimex ESP32-POE",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 15,
|
|
||||||
"mosi": 2,
|
|
||||||
"clk": 14,
|
|
||||||
"irq": 13,
|
|
||||||
"en": 16,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": true,
|
|
||||||
"phy_addr": 0,
|
|
||||||
"power": 12,
|
|
||||||
"mdc": 23,
|
|
||||||
"mdio": 18,
|
|
||||||
"type": 0,
|
|
||||||
"clk_mode": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
74
docs/DeviceProfiles/liligo_t-eth-lite_poe.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE CMT",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 15,
|
||||||
|
"cs": 32,
|
||||||
|
"fcs": 33,
|
||||||
|
"sdio": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24 + Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 32,
|
||||||
|
"clk": 33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
47
docs/DeviceProfiles/olimex_esp32_gateway.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Olimex ESP32-Gateway",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 14,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 12,
|
||||||
|
"irq": 15,
|
||||||
|
"en": 2,
|
||||||
|
"cs": 4
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Olimex ESP32-Gateway with SSH1106",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 14,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 12,
|
||||||
|
"irq": 15,
|
||||||
|
"en": 2,
|
||||||
|
"cs": 4
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 32,
|
||||||
|
"clk": 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1",
|
"name": "OpenDTU Fusion v1",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -25,6 +28,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -54,6 +60,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -83,6 +92,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -115,6 +127,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -152,6 +167,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -186,5 +204,122 @@
|
|||||||
"data": 2,
|
"data": 2,
|
||||||
"clk": 1
|
"clk": 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 0,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE with SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE with SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -22,6 +22,34 @@
|
|||||||
"clk_mode": 0
|
"clk_mode": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "WT32-ETH01 with SH1106",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 4,
|
||||||
|
"mosi": 2,
|
||||||
|
"clk": 32,
|
||||||
|
"irq": 33,
|
||||||
|
"en": 14,
|
||||||
|
"cs": 15
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 1,
|
||||||
|
"power": 16,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 5,
|
||||||
|
"clk": 17
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "WT32-ETH01 with SSD1306",
|
"name": "WT32-ETH01 with SSD1306",
|
||||||
"links": [
|
"links": [
|
||||||
|
|||||||
@ -1,20 +1,3 @@
|
|||||||
# Display integration
|
# Display integration
|
||||||
|
|
||||||
OpenDTU currently supports 3 types of displays (SSD1306, SH1106 and PCD8544). Currently only displays with a resolution of 128x64 pixel are supported. To activate a display you have to specify it's type and pin assignment either in the `platformio_override.ini` or in a device profile. Due to the fact that device profiles work with the pre-compiled binary the following documentation will only cover the device profile method.
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/hardware/display/>
|
||||||
|
|
||||||
You can either create your own device profile as described [here](DeviceProfiles.md) or use some pre-defined. The pre-defined profiles can be found [here](DeviceProfiles/). You can simply open the json file with a text editor of your choice to view/edit the pin assignment.
|
|
||||||
|
|
||||||
## Uploading Device Profiles
|
|
||||||
|
|
||||||
Use the "Config Management" site to upload (Restore) the json file. Make sure to choose "Pin Mapping (pin_mapping.json)" in the combo box. After you click on restore the ESP will restart. At this point, the profile is not yet active. Please read the next chapter.
|
|
||||||

|
|
||||||
|
|
||||||
## Selecting a Device Profile
|
|
||||||
|
|
||||||
After you uploaded the device profile you can select the profile in the "Device Manager" view. After a click on "Save" the ESP will be restarted and the pin assignment is active. At this point the display should already show something. Please see the next chapter for display settings.
|
|
||||||

|
|
||||||
|
|
||||||
## Display Settings
|
|
||||||
|
|
||||||
Display settings can also be found in the "Device Manager".
|
|
||||||

|
|
||||||
|
|||||||
@ -1,275 +0,0 @@
|
|||||||
<mxfile host="65bd71144e">
|
|
||||||
<diagram name="Page-1" id="b5b7bab2-c9e2-2cf4-8b2a-24fd1a2a6d21">
|
|
||||||
<mxGraphModel dx="1370" dy="985" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="0"/>
|
|
||||||
<mxCell id="1" parent="0"/>
|
|
||||||
<mxCell id="6e0c8c40b5770093-72" value="" style="shape=folder;fontStyle=1;spacingTop=10;tabWidth=194;tabHeight=22;tabPosition=left;html=1;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fillColor=none;fontFamily=Verdana;fontSize=10;align=center;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="150" y="114.5" width="1090" height="1065.5" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="6e0c8c40b5770093-73" value="<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Menlo, Monaco, &quot;Courier New&quot;, monospace; font-size: 12px; line-height: 18px;"><span style="color: #4ec9b0;">PowerLimiterClass</span>::<span style="color: #dcdcaa;">loop</span>()</div>" style="text;html=1;align=left;verticalAlign=top;spacingTop=-4;fontSize=10;fontFamily=Verdana" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="150" y="114.5" width="130" height="20" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="sqCMRMHiXPc9LqBIY9SA-1" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="475" y="50" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="sqCMRMHiXPc9LqBIY9SA-2" value="discover state on initial call or after enabling powerLimiter" style="html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-1" target="2" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="205" y="370" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="sqCMRMHiXPc9LqBIY9SA-6" value="<p style="margin:0px;margin-top:4px;text-align:center;">STATE_OFF</p><hr><p></p><p style="margin:0px;margin-left:8px;text-align:left;">entry / stop inverter, limit lower limit<br>do / nothing<br>exit / nothing</p>" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="170" y="585" width="200" height="100" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="sqCMRMHiXPc9LqBIY9SA-7" value="<p style="margin:0px;margin-top:4px;text-align:center;">STATE_CONSUME_SOLAR_POWER_ONLY</p><hr><p></p><p style="margin:0px;margin-left:8px;text-align:left;">entry /<br>do / setNewLimit<br>exit /</p>" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="525" y="590" width="270" height="90" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="sqCMRMHiXPc9LqBIY9SA-8" value="<p style="margin:0px;margin-top:4px;text-align:center;">STATE_NORMAL_OPERATION</p><hr><p></p><p style="margin:0px;margin-left:8px;text-align:left;">entry /<br>do / setNewLimit<br>exit /</p>" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="970" y="585" width="200" height="100" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="2" value="<p style="margin:0px;margin-top:4px;text-align:center;">STATE_DISOVER</p><hr><p></p><p style="margin:0px;margin-left:8px;text-align:left;">entry /&nbsp;<br>do / nothing<br>exit /&nbsp;</p>" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="390" y="180" width="200" height="100" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="5" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="414" y="780" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="7" value="<p><span style="font-size: 11px; background-color: rgb(255, 255, 255);">!Inverter-&gt;isProducing || isStopThresholdReached</span></p>" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="425" y="300" width="130" height="130" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="10" value="<span style="font-size: 11px; background-color: rgb(255, 255, 255);">canUseDirectSolarPower</span>" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="595" y="390" width="130" height="120" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="11" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="10" target="sqCMRMHiXPc9LqBIY9SA-7" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="620" y="540" as="sourcePoint"/>
|
|
||||||
<mxPoint x="670" y="490" as="targetPoint"/>
|
|
||||||
<Array as="points"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="20" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="11" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="0.075" y="1" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="12" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="2" target="7" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="484" y="280" as="sourcePoint"/>
|
|
||||||
<mxPoint x="420" y="220" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="13" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;" parent="1" source="7" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="570" y="290" as="sourcePoint"/>
|
|
||||||
<mxPoint x="400" y="330" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="270" y="365"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="14" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="13" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="0.3049" relative="1" as="geometry">
|
|
||||||
<mxPoint x="80" y="-105" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="15" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="7" target="10" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="360" y="370" as="sourcePoint"/>
|
|
||||||
<mxPoint x="280" y="595" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="660" y="365"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="16" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="15" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="0.3049" relative="1" as="geometry">
|
|
||||||
<mxPoint x="-45" y="-15" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="18" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="10" target="sqCMRMHiXPc9LqBIY9SA-8" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="760" y="340" as="sourcePoint"/>
|
|
||||||
<mxPoint x="810" y="290" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="19" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="18" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="0.142" y="2" relative="1" as="geometry">
|
|
||||||
<mxPoint x="-239" y="-8" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="21" value="isStopThresholdReached" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="220" y="730" width="130" height="130" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="22" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="21" target="24" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="620" y="540" as="sourcePoint"/>
|
|
||||||
<mxPoint x="670" y="490" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="285" y="995"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="31" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="22" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.702" y="1" relative="1" as="geometry">
|
|
||||||
<mxPoint x="39" y="91" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="23" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.581;exitY=0.998;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-6" target="21" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="480" y="810" as="sourcePoint"/>
|
|
||||||
<mxPoint x="530" y="760" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="24" target="25">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="42" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="41">
|
|
||||||
<mxGeometry x="0.36" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="24" value="canUseDirectSolarPower" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="600" y="930" width="130" height="130" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="25" value="isStartThresholdReached" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="830" y="930" width="130" height="130" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="29" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="21" target="5" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="350" y="830" as="sourcePoint"/>
|
|
||||||
<mxPoint x="400" y="780" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="30" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="29" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.1" relative="1" as="geometry">
|
|
||||||
<mxPoint x="3" y="-15" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-38" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-7" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="570" y="820" as="sourcePoint"/>
|
|
||||||
<mxPoint x="620" y="770" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-39" value="isStopThresholdReached ||<br>(!canUseDirectSolarPower <br>&amp;&amp; EMPTY_WHEN_FULL)" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-38" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.2129" y="-2" relative="1" as="geometry">
|
|
||||||
<mxPoint x="-14" y="-33" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-40" value="isStartThresholdReached ||<br style="border-color: var(--border-color);">(!canUseDirectSolarPower<br style="border-color: var(--border-color);">&amp;&amp; EMPTY_AT_NIGHT)" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-7" target="sqCMRMHiXPc9LqBIY9SA-8" edge="1">
|
|
||||||
<mxGeometry x="-0.0286" y="25" width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="570" y="560" as="sourcePoint"/>
|
|
||||||
<mxPoint x="620" y="510" as="targetPoint"/>
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-41" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.22;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.354;exitY=1.025;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-8" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1040" y="680" as="sourcePoint"/>
|
|
||||||
<mxPoint x="890" y="750" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="1040" y="1070"/>
|
|
||||||
<mxPoint x="214" y="1070"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-42" value="isStopThresholdReached ||" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-41" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.2129" y="-2" relative="1" as="geometry">
|
|
||||||
<mxPoint x="-151" y="32" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-43" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="1055" y="799.7" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-44" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-43" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1069.0000000000002" y="685" as="sourcePoint"/>
|
|
||||||
<mxPoint x="1119" y="749.7" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-45" value="inTargetRange do nothing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-44" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.1" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-46" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="530" y="795" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-47" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-46" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="544" y="680" as="sourcePoint"/>
|
|
||||||
<mxPoint x="594" y="745" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-48" value="after setNewLimit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-47" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.1" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-49" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="1110" y="800" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-50" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-49" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="1124.0000000000002" y="685.3" as="sourcePoint"/>
|
|
||||||
<mxPoint x="1174" y="750" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="JKiNQljIdbqBsyxyxwz1-51" value="after setNewLimit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-50" vertex="1" connectable="0">
|
|
||||||
<mxGeometry x="-0.1" relative="1" as="geometry">
|
|
||||||
<mxPoint x="16" y="33" as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="38" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="24" target="sqCMRMHiXPc9LqBIY9SA-7">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="580" y="710" as="sourcePoint"/>
|
|
||||||
<mxPoint x="630" y="660" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="39" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="38">
|
|
||||||
<mxGeometry x="0.104" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="40" value="" style="endArrow=classic;html=1;entryX=1;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" target="sqCMRMHiXPc9LqBIY9SA-7">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="970" y="658" as="sourcePoint"/>
|
|
||||||
<mxPoint x="800" y="660" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="48" value="canUseDirectSolarPower<br style="border-color: var(--border-color);">&amp;&amp; EMPTY_AT_NIGHT" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="40">
|
|
||||||
<mxGeometry x="-0.04" y="1" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="43" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="25" target="sqCMRMHiXPc9LqBIY9SA-8">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="960" y="980" as="sourcePoint"/>
|
|
||||||
<mxPoint x="1010" y="930" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="1020" y="995"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="44" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="43">
|
|
||||||
<mxGeometry x="0.1135" y="2" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="45" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="880" y="850" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="46" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="25" target="45">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="580" y="710" as="sourcePoint"/>
|
|
||||||
<mxPoint x="630" y="660" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="47" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="46">
|
|
||||||
<mxGeometry x="-0.08" relative="1" as="geometry">
|
|
||||||
<mxPoint as="offset"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
</mxfile>
|
|
||||||
|
Before Width: | Height: | Size: 147 KiB |
@ -1,21 +1,3 @@
|
|||||||
# Upgrade Partition
|
# Upgrade Partition
|
||||||
|
|
||||||
To be able to install further updates you have to update the partition table of the ESP32. Doing so will **erase** all configuration data. Over The Air update using the web interface is **NOT** possible!
|
This documentation has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/howto/upgrade_partition/>
|
||||||
|
|
||||||
**So make sure you export a backup of your configuration files before continuing.**
|
|
||||||
|
|
||||||
There are several possibilities to update the partition table:
|
|
||||||
|
|
||||||
- Using Visual Studio Code or PlatformIO CLI
|
|
||||||
|
|
||||||
If you have already used Visual Studio Code or the `platformio` command you can use it again to install the latest version. The partition table is upgraded automatically.
|
|
||||||
|
|
||||||
- Any kind of flash interface
|
|
||||||
|
|
||||||
If you like to use any kind of flash interface like `esptool.py`, Espressif Flash Download Tool, ESP_Flasher or esptool-js you have to make sure to upload the provided .factory.bin file. It is important to enter the correct target address.
|
|
||||||
|
|
||||||
| Address | File |
|
|
||||||
| ---------| ---------------------- |
|
|
||||||
| 0x0 | opendtu-*.factory.bin |
|
|
||||||
|
|
||||||
After upgrading the ESP32 will open the intergrated access point (AP) again. Just connect to it using the default password ("openDTU42"). If you are connected, just visit <http://192.168.4.1> and enter the "Configuration Management". Recover the previously backuped config files.
|
|
||||||
|
|||||||
@ -1,44 +1,3 @@
|
|||||||
# Web API
|
# Web API
|
||||||
|
|
||||||
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/>
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/>
|
||||||
|
|
||||||
## List of URLs
|
|
||||||
|
|
||||||
This list may be incomplete
|
|
||||||
|
|
||||||
| GET/POST | Auth required | URL |
|
|
||||||
| -------- | --- | -- |
|
|
||||||
| Get | no | /api/vedirectlivedata/status |
|
|
||||||
| Get | no | /api/vedirect/status |
|
|
||||||
| Get | no | /api/huawei/status |
|
|
||||||
| Get | no | /api/huawei/config |
|
|
||||||
| Get | no | /api/huawei/limit/config |
|
|
||||||
| Get | no | /api/batterylivedata/status |
|
|
||||||
| Get | no | /api/battery/status |
|
|
||||||
| Get | no | /api/powerlimiter/status |
|
|
||||||
|
|
||||||
### Victron REST-API (/api/vedirectlivedata/status):
|
|
||||||
````JSON
|
|
||||||
{
|
|
||||||
"data_age":0,
|
|
||||||
"age_critical":false,
|
|
||||||
"PID":"SmartSolar MPPT 100|30",
|
|
||||||
"SER":"XXX",
|
|
||||||
"FW":"159",
|
|
||||||
"LOAD":"ON",
|
|
||||||
"CS":"Bulk",
|
|
||||||
"ERR":"No error",
|
|
||||||
"OR":"Not off",
|
|
||||||
"MPPT":"MPP Tracker active",
|
|
||||||
"HSDS":{"v":46,"u":"Days"},
|
|
||||||
"V":{"v":26.36,"u":"V"},
|
|
||||||
"I":{"v":3.4,"u":"A"},
|
|
||||||
"VPV":{"v":37.13,"u":"V"},
|
|
||||||
"PPV":{"v":93,"u":"W"},
|
|
||||||
"H19":{"v":83.16,"u":"kWh"},
|
|
||||||
"H20":{"v":1.39,"u":"kWh"},
|
|
||||||
"H21":{"v":719,"u":"W"},
|
|
||||||
"H22":{"v":1.43,"u":"kWh"},
|
|
||||||
"H23":{"v":737,"u":"W"}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 261 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 3.9 MiB |
@ -1,191 +0,0 @@
|
|||||||
# Hardware, building and flashing Firmware
|
|
||||||
|
|
||||||
## Hardware you need
|
|
||||||
|
|
||||||
### ESP32 board
|
|
||||||
|
|
||||||
For ease of use, buy a "ESP32 DEVKIT DOIT" or "ESP32 NodeMCU Development Board" with an ESP32-S3 or ESP-WROOM-32 chipset on it.
|
|
||||||
|
|
||||||
Sample Picture:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware)
|
|
||||||
|
|
||||||
### NRF24L01+ radio board (See inverter table above for supported inverters)
|
|
||||||
|
|
||||||
The PLUS sign is IMPORTANT! There are different variants available, with antenna on the printed circuit board or external antenna.
|
|
||||||
|
|
||||||
Sample picture:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Buy your hardware from a trusted source, at best from a dealer/online shop in your country where you have support and the right to return non-functional hardware.
|
|
||||||
When you want to buy from Amazon, AliExpress, eBay etc., take note that there is a lot of low-quality or fake hardware offered. Read customer comments and ratings carefully!
|
|
||||||
|
|
||||||
A heavily incomplete list of trusted hardware shops in Germany is:
|
|
||||||
|
|
||||||
* [AZ-Delivery](https://www.az-delivery.de/)
|
|
||||||
* [Makershop](https://www.makershop.de/)
|
|
||||||
* [Berrybase](https://www.berrybase.de/)
|
|
||||||
|
|
||||||
This list is for your convenience only, the project is not related to any of these shops.
|
|
||||||
|
|
||||||
### CMT2300A radio board (See inverter table above for supported inverters)
|
|
||||||
|
|
||||||
It is important to get a module which supports SPI communication. The following modules are currently supported:
|
|
||||||
|
|
||||||
* EBYTE E49-900M20S
|
|
||||||
|
|
||||||
The CMT2300A uses 3-Wire half duplex SPI communication. Due to this fact it currently requires a separate SPI bus. If you want to run the CMT2300A module on the same ESP32 as a NRF24L01+ module or a PCD8544 display make sure you get a ESP which supports 2 SPI busses. Currently the SPI bus host is hardcoded to number 2. This may change in future.
|
|
||||||
|
|
||||||
### 3.3V / 5V logic level converter
|
|
||||||
|
|
||||||
The logic level converter is used to interface with the Victron MPPT charge controller and the relay board. It converts the 3.3V logic level used by the ESP32 to 5V logic used by the other devices.
|
|
||||||
|
|
||||||
### SN65HVD230 CAN bus transceiver
|
|
||||||
|
|
||||||
The SN65HVD230 CAN bus transceiver is used to interface with the Pylontech battery. It leverages the CAN bus controller of the ESP32. This CAN bus operates at 500kbit/s
|
|
||||||
|
|
||||||
### MCP2515 CAN bus module
|
|
||||||
|
|
||||||
The MCP2515 CAN bus module consists of a CAN bus controller and a CAN bus transceiver and is used to interface with the Huawei AC charger. This CAN bus operates at 125kbit/s. The module is connected via SPI and currently requires a separate SPI bus. If you want to use the Huawei AC charger make sure to get an ESP which supports 2 SPI busses. Currently the SPI bus host is hardcoded to number 2. This may change in future. Please note: Using the Huawei AC charger in combination with the CMT2300A radio board is not supported at the moment.
|
|
||||||
|
|
||||||
MCP2515 CAN bus modules that are widely available are designed for 5V supply voltage. To make them work with 3.3V / the ESP32 a modification is required. [This modification is described here.](https://forums.raspberrypi.com/viewtopic.php?t=141052)
|
|
||||||
|
|
||||||
### Relay module
|
|
||||||
|
|
||||||
The Huawei PSU can be switched on / off using the slot detect port. This is done by this relay.
|
|
||||||
|
|
||||||
### Power supply
|
|
||||||
|
|
||||||
Use a power supply with 5 V and 1 A. The USB cable connected to your PC/Notebook may be powerful enough or may be not.
|
|
||||||
|
|
||||||
## Wiring up the NRF24L01+ module
|
|
||||||
|
|
||||||
### Schematic
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Symbolic view
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Change pin assignment
|
|
||||||
|
|
||||||
Its possible to change all the pins of the NRF24L01+ module, the Display, the LED etc.
|
|
||||||
The recommend way to change the pin assignment is by creating a custom [device profile](DeviceProfiles.md).
|
|
||||||
It is also possible to create a custom environment and compile the source yourself. This can be achieved by copying one of the [env:....] sections from 'platformio.ini' to 'platformio_override.ini' and editing the 'platformio_override.ini' file and add/change one or more of the following lines to the 'build_flags' parameter:
|
|
||||||
|
|
||||||
```makefile
|
|
||||||
-DHOYMILES_PIN_MISO=19
|
|
||||||
-DHOYMILES_PIN_MOSI=23
|
|
||||||
-DHOYMILES_PIN_SCLK=18
|
|
||||||
-DHOYMILES_PIN_IRQ=16
|
|
||||||
-DHOYMILES_PIN_CE=4
|
|
||||||
-DHOYMILES_PIN_CS=5
|
|
||||||
-DVICTRON_PIN_TX=21
|
|
||||||
-DVICTRON_PIN_RX=22
|
|
||||||
-DPYLONTECH_PIN_RX=27
|
|
||||||
-DPYLONTECH_PIN_TX=14
|
|
||||||
-DHUAWEI_PIN_MISO=12
|
|
||||||
-DHUAWEI_PIN_MOSI=13
|
|
||||||
-DHUAWEI_PIN_SCLK=26
|
|
||||||
-DHUAWEI_PIN_IRQ=25
|
|
||||||
-DHUAWEI_PIN_CS=15
|
|
||||||
-DHUAWEI_PIN_POWER=33
|
|
||||||
```
|
|
||||||
|
|
||||||
It is recommended to make all changes only in the 'platformio_override.ini', this is your personal copy.
|
|
||||||
|
|
||||||
## Flashing and starting up
|
|
||||||
|
|
||||||
### with Visual Studio Code
|
|
||||||
|
|
||||||
* Install [Visual Studio Code](https://code.visualstudio.com/download) (from now named "vscode")
|
|
||||||
* In Visual Studio Code, install the [PlatformIO Extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide)
|
|
||||||
* Install git and enable git in vscode - [git download](https://git-scm.com/downloads/) - [Instructions](https://www.jcchouinard.com/install-git-in-vscode/)
|
|
||||||
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur): Inside vscode open the command palette by pressing `CTRL` + `SHIFT` + `P`. Enter `git clone`, add the repository-URL `https://github.com/tbnobody/OpenDTU`. Next you have to choose (or create) a target directory.
|
|
||||||
* In vscode, choose File --> Open Folder and select the previously downloaded source code. (You have to select the folder which contains the "platformio.ini" and "platformio_override.ini" file)
|
|
||||||
* Adjust the COM port in the file "platformio_override.ini" for your USB-to-serial-converter. It occurs twice:
|
|
||||||
* upload_port
|
|
||||||
* monitor_port
|
|
||||||
* Select the arrow button in the blue bottom status bar (PlatformIO: Upload) to compile and upload the firmware. During the compilation, all required libraries are downloaded automatically.
|
|
||||||
* Under Linux, if the upload fails with error messages "Could not open /dev/ttyUSB0, the port doesn't exist", you can check via ```ls -la /dev/tty*``` to which group your port belongs to, and then add your user this group via ```sudo adduser <yourusername> dialout``` (if you are using ```arch-linux``` use: ```sudo gpasswd -a <yourusername> uucp```, this method requires a logout/login of the affected user).
|
|
||||||
* There are two videos showing these steps:
|
|
||||||
* [Git Clone and compilation](https://youtu.be/9cA_esv3zeA)
|
|
||||||
* [Full installation and compilation](https://youtu.be/xs6TqHn7QWM)
|
|
||||||
|
|
||||||
### on the commandline with PlatformIO Core
|
|
||||||
|
|
||||||
* Install [PlatformIO Core](https://platformio.org/install/cli)
|
|
||||||
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur)
|
|
||||||
* Adjust the COM port in the file "platformio_override.ini". It occurs twice:
|
|
||||||
* upload_port
|
|
||||||
* monitor_port
|
|
||||||
* build: `platformio run -e generic`
|
|
||||||
* upload to esp module: `platformio run -e generic -t upload`
|
|
||||||
* other options:
|
|
||||||
* clean the sources: `platformio run -e generic -t clean`
|
|
||||||
* erase flash: `platformio run -e generic -t erase`
|
|
||||||
|
|
||||||
### using the pre-compiled .bin files
|
|
||||||
|
|
||||||
The pre-compiled files can be found on the [github page](https://github.com/tbnobody/OpenDTU) in the tab "Actions" and the sub menu "OpenDTU Build". Just choose the latest build from the master branch (search for "master" in the blue font text but click on the white header text!). You need to be logged in with your github account to download the files.
|
|
||||||
Use a ESP32 flash tool of your choice (see next chapter) and flash the `.bin` files to the right addresses:
|
|
||||||
|
|
||||||
| Address | File |
|
|
||||||
| ---------| ---------------------- |
|
|
||||||
| 0x1000 | bootloader.bin |
|
|
||||||
| 0x8000 | partitions.bin |
|
|
||||||
| 0xe000 | boot_app0.bin |
|
|
||||||
| 0x10000 | opendtu-*.bin |
|
|
||||||
|
|
||||||
For further updates you can just use the web interface and upload the `opendtu-*.bin` file.
|
|
||||||
|
|
||||||
#### Flash with esptool.py (Linux)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset \
|
|
||||||
write_flash --flash_mode dout --flash_freq 40m --flash_size detect \
|
|
||||||
0x1000 bootloader.bin \
|
|
||||||
0x8000 partitions.bin \
|
|
||||||
0xe000 boot_app0.bin \
|
|
||||||
0x10000 opendtu-generic.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Flash with Espressif Flash Download Tool (Windows)
|
|
||||||
|
|
||||||
[Download link](https://www.espressif.com/en/support/download/other-tools)
|
|
||||||
|
|
||||||
* On startup, select Chip Type -> "ESP32" / WorkMode -> "Develop"
|
|
||||||
* Prepare all settings (see picture). Make sure to uncheck the `DoNotChgBin` option. Otherwise you may get errors like "invalid header".
|
|
||||||
* 
|
|
||||||
* Press "Erase" button on screen. Look into the terminal window, you should see dots appear. Then press the "Boot" button on the ESP32 board. Wait for "FINISH" to see if flashing/erasing is done.
|
|
||||||
* To program, press "Start" on screen, then the "Boot" button.
|
|
||||||
* When flashing is complete (FINISH appears) then press the Reset button on the ESP32 board (or powercycle ) to start the OpenDTU application.
|
|
||||||
|
|
||||||
#### Flash with ESP_Flasher (Windows)
|
|
||||||
|
|
||||||
Users report that [ESP_Flasher](https://github.com/Jason2866/ESP_Flasher/releases/) is suitable for flashing OpenDTU on Windows.
|
|
||||||
|
|
||||||
#### Flash with [ESP_Flasher](https://espressif.github.io/esptool-js/) - web version
|
|
||||||
|
|
||||||
It is also possible to flash it via the web tools which might be more convenient and is platform independent.
|
|
||||||
|
|
||||||
## Flashing an Update using "Over The Air" OTA Update
|
|
||||||
|
|
||||||
Once you have your OpenDTU running and connected to WLAN, you can do further updates through the web interface.
|
|
||||||
Navigate to Settings --> Firmware upgrade and press the browse button. Select the firmware file from your local computer.
|
|
||||||
|
|
||||||
You'll find the firmware file (after a successful build process) under `.pio/build/generic/firmware.bin`.
|
|
||||||
|
|
||||||
If you downloaded a precompiled zip archive, unpack it and choose `opendtu-generic.bin`.
|
|
||||||
|
|
||||||
After the successful upload, the OpenDTU immediately restarts into the new firmware.
|
|
||||||
|
|
||||||
|
|
||||||
## Builds
|
|
||||||
|
|
||||||
Different builds from existing installations can be found here [Builds](builds/README.md)
|
|
||||||
Like to show your own build? Just send me a Pull Request.
|
|
||||||
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 90 KiB |
@ -1,37 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#include "BatteryStats.h"
|
|
||||||
|
|
||||||
class BatteryProvider {
|
|
||||||
public:
|
|
||||||
// returns true if the provider is ready for use, false otherwise
|
|
||||||
virtual bool init(bool verboseLogging) = 0;
|
|
||||||
|
|
||||||
virtual void deinit() = 0;
|
|
||||||
virtual void loop() = 0;
|
|
||||||
virtual std::shared_ptr<BatteryStats> getStats() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BatteryClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler&);
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
std::shared_ptr<BatteryStats const> getStats() const;
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
uint32_t _lastMqttPublish = 0;
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
std::unique_ptr<BatteryProvider> _upProvider = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern BatteryClass Battery;
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "AsyncJson.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "JkBmsDataPoints.h"
|
|
||||||
#include "VeDirectShuntController.h"
|
|
||||||
|
|
||||||
// mandatory interface for all kinds of batteries
|
|
||||||
class BatteryStats {
|
|
||||||
public:
|
|
||||||
String const& getManufacturer() const { return _manufacturer; }
|
|
||||||
|
|
||||||
// the last time *any* datum was updated
|
|
||||||
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
|
||||||
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }
|
|
||||||
|
|
||||||
uint8_t getSoC() const { return _SoC; }
|
|
||||||
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
|
||||||
|
|
||||||
// convert stats to JSON for web application live view
|
|
||||||
virtual void getLiveViewData(JsonVariant& root) const;
|
|
||||||
|
|
||||||
virtual void mqttPublish() const;
|
|
||||||
|
|
||||||
bool isValid() const { return _lastUpdateSoC > 0 && _lastUpdate > 0; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
String _manufacturer = "unknown";
|
|
||||||
uint8_t _SoC = 0;
|
|
||||||
uint32_t _lastUpdateSoC = 0;
|
|
||||||
uint32_t _lastUpdate = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PylontechBatteryStats : public BatteryStats {
|
|
||||||
friend class PylontechCanReceiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); }
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
|
||||||
|
|
||||||
float _chargeVoltage;
|
|
||||||
float _chargeCurrentLimitation;
|
|
||||||
float _dischargeCurrentLimitation;
|
|
||||||
uint16_t _stateOfHealth;
|
|
||||||
float _voltage; // total voltage of the battery pack
|
|
||||||
// total current into (positive) or from (negative)
|
|
||||||
// the battery, i.e., the charging current
|
|
||||||
float _current;
|
|
||||||
float _temperature;
|
|
||||||
|
|
||||||
bool _alarmOverCurrentDischarge;
|
|
||||||
bool _alarmOverCurrentCharge;
|
|
||||||
bool _alarmUnderTemperature;
|
|
||||||
bool _alarmOverTemperature;
|
|
||||||
bool _alarmUnderVoltage;
|
|
||||||
bool _alarmOverVoltage;
|
|
||||||
bool _alarmBmsInternal;
|
|
||||||
|
|
||||||
bool _warningHighCurrentDischarge;
|
|
||||||
bool _warningHighCurrentCharge;
|
|
||||||
bool _warningLowTemperature;
|
|
||||||
bool _warningHighTemperature;
|
|
||||||
bool _warningLowVoltage;
|
|
||||||
bool _warningHighVoltage;
|
|
||||||
bool _warningBmsInternal;
|
|
||||||
|
|
||||||
bool _chargeEnabled;
|
|
||||||
bool _dischargeEnabled;
|
|
||||||
bool _chargeImmediately;
|
|
||||||
};
|
|
||||||
|
|
||||||
class JkBmsBatteryStats : public BatteryStats {
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final {
|
|
||||||
getJsonData(root, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void getInfoViewData(JsonVariant& root) const {
|
|
||||||
getJsonData(root, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqttPublish() const final;
|
|
||||||
|
|
||||||
void updateFrom(JkBms::DataPointContainer const& dp);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void getJsonData(JsonVariant& root, bool verbose) const;
|
|
||||||
|
|
||||||
JkBms::DataPointContainer _dataPoints;
|
|
||||||
mutable uint32_t _lastMqttPublish = 0;
|
|
||||||
mutable uint32_t _lastFullMqttPublish = 0;
|
|
||||||
|
|
||||||
uint16_t _cellMinMilliVolt = 0;
|
|
||||||
uint16_t _cellAvgMilliVolt = 0;
|
|
||||||
uint16_t _cellMaxMilliVolt = 0;
|
|
||||||
uint32_t _cellVoltageTimestamp = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VictronSmartShuntStats : public BatteryStats {
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
|
|
||||||
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
|
|
||||||
|
|
||||||
private:
|
|
||||||
float _voltage;
|
|
||||||
float _current;
|
|
||||||
float _temperature;
|
|
||||||
bool _tempPresent;
|
|
||||||
uint8_t _chargeCycles;
|
|
||||||
uint32_t _timeToGo;
|
|
||||||
float _chargedEnergy;
|
|
||||||
float _dischargedEnergy;
|
|
||||||
String _modelName;
|
|
||||||
|
|
||||||
bool _alarmLowVoltage;
|
|
||||||
bool _alarmHighVoltage;
|
|
||||||
bool _alarmLowSOC;
|
|
||||||
bool _alarmLowTemperature;
|
|
||||||
bool _alarmHighTemperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MqttBatteryStats : public BatteryStats {
|
|
||||||
public:
|
|
||||||
// since the source of information was MQTT in the first place,
|
|
||||||
// we do NOT publish the same data under a different topic.
|
|
||||||
void mqttPublish() const final { }
|
|
||||||
|
|
||||||
// the SoC is the only interesting value in this case, which is already
|
|
||||||
// displayed at the top of the live view. do not generate a card.
|
|
||||||
void getLiveViewData(JsonVariant& root) const final { }
|
|
||||||
|
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = _lastUpdate = millis(); }
|
|
||||||
};
|
|
||||||
@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#define CONFIG_FILENAME "/config.json"
|
#define CONFIG_FILENAME "/config.json"
|
||||||
#define CONFIG_VERSION 0x00011b00 // 0.1.27 // make sure to clean all after change
|
#define CONFIG_VERSION 0x00011d00 // 0.1.29 // make sure to clean all after change
|
||||||
|
|
||||||
#define WIFI_MAX_SSID_STRLEN 32
|
#define WIFI_MAX_SSID_STRLEN 32
|
||||||
#define WIFI_MAX_PASSWORD_STRLEN 64
|
#define WIFI_MAX_PASSWORD_STRLEN 64
|
||||||
@ -16,30 +19,21 @@
|
|||||||
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
||||||
|
|
||||||
#define MQTT_MAX_HOSTNAME_STRLEN 128
|
#define MQTT_MAX_HOSTNAME_STRLEN 128
|
||||||
|
#define MQTT_MAX_CLIENTID_STRLEN 64
|
||||||
#define MQTT_MAX_USERNAME_STRLEN 64
|
#define MQTT_MAX_USERNAME_STRLEN 64
|
||||||
#define MQTT_MAX_PASSWORD_STRLEN 64
|
#define MQTT_MAX_PASSWORD_STRLEN 64
|
||||||
#define MQTT_MAX_TOPIC_STRLEN 256
|
#define MQTT_MAX_TOPIC_STRLEN 32
|
||||||
#define MQTT_MAX_LWTVALUE_STRLEN 20
|
#define MQTT_MAX_LWTVALUE_STRLEN 20
|
||||||
#define MQTT_MAX_CERT_STRLEN 2560
|
#define MQTT_MAX_CERT_STRLEN 2560
|
||||||
|
|
||||||
#define INV_MAX_NAME_STRLEN 31
|
#define INV_MAX_NAME_STRLEN 31
|
||||||
#define INV_MAX_COUNT 5
|
#define INV_MAX_COUNT 10
|
||||||
#define INV_MAX_CHAN_COUNT 6
|
#define INV_MAX_CHAN_COUNT 6
|
||||||
|
|
||||||
#define CHAN_MAX_NAME_STRLEN 31
|
#define CHAN_MAX_NAME_STRLEN 31
|
||||||
|
|
||||||
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
||||||
|
#define LOCALE_STRLEN 2
|
||||||
#define POWERMETER_MAX_PHASES 3
|
|
||||||
#define POWERMETER_MAX_HTTP_URL_STRLEN 1024
|
|
||||||
#define POWERMETER_MAX_USERNAME_STRLEN 64
|
|
||||||
#define POWERMETER_MAX_PASSWORD_STRLEN 64
|
|
||||||
#define POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN 64
|
|
||||||
#define POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN 256
|
|
||||||
#define POWERMETER_MAX_HTTP_JSON_PATH_STRLEN 256
|
|
||||||
#define POWERMETER_HTTP_TIMEOUT 1000
|
|
||||||
|
|
||||||
#define JSON_BUFFER_SIZE 15360
|
|
||||||
|
|
||||||
struct CHANNEL_CONFIG_T {
|
struct CHANNEL_CONFIG_T {
|
||||||
uint16_t MaxChannelPower;
|
uint16_t MaxChannelPower;
|
||||||
@ -58,23 +52,11 @@ struct INVERTER_CONFIG_T {
|
|||||||
uint8_t ReachableThreshold;
|
uint8_t ReachableThreshold;
|
||||||
bool ZeroRuntimeDataIfUnrechable;
|
bool ZeroRuntimeDataIfUnrechable;
|
||||||
bool ZeroYieldDayOnMidnight;
|
bool ZeroYieldDayOnMidnight;
|
||||||
|
bool ClearEventlogOnMidnight;
|
||||||
bool YieldDayCorrection;
|
bool YieldDayCorrection;
|
||||||
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Auth { none, basic, digest };
|
|
||||||
struct POWERMETER_HTTP_PHASE_CONFIG_T {
|
|
||||||
bool Enabled;
|
|
||||||
char Url[POWERMETER_MAX_HTTP_URL_STRLEN + 1];
|
|
||||||
Auth AuthType;
|
|
||||||
char Username[POWERMETER_MAX_USERNAME_STRLEN +1];
|
|
||||||
char Password[POWERMETER_MAX_USERNAME_STRLEN +1];
|
|
||||||
char HeaderKey[POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN + 1];
|
|
||||||
char HeaderValue[POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN + 1];
|
|
||||||
uint16_t Timeout;
|
|
||||||
char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CONFIG_T {
|
struct CONFIG_T {
|
||||||
struct {
|
struct {
|
||||||
uint32_t Version;
|
uint32_t Version;
|
||||||
@ -110,8 +92,8 @@ struct CONFIG_T {
|
|||||||
struct {
|
struct {
|
||||||
bool Enabled;
|
bool Enabled;
|
||||||
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
||||||
bool VerboseLogging;
|
|
||||||
uint32_t Port;
|
uint32_t Port;
|
||||||
|
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
|
||||||
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
||||||
char Password[MQTT_MAX_PASSWORD_STRLEN + 1];
|
char Password[MQTT_MAX_PASSWORD_STRLEN + 1];
|
||||||
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
@ -154,7 +136,6 @@ struct CONFIG_T {
|
|||||||
uint32_t Frequency;
|
uint32_t Frequency;
|
||||||
uint8_t CountryMode;
|
uint8_t CountryMode;
|
||||||
} Cmt;
|
} Cmt;
|
||||||
bool VerboseLogging;
|
|
||||||
} Dtu;
|
} Dtu;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -167,7 +148,7 @@ struct CONFIG_T {
|
|||||||
bool ScreenSaver;
|
bool ScreenSaver;
|
||||||
uint8_t Rotation;
|
uint8_t Rotation;
|
||||||
uint8_t Contrast;
|
uint8_t Contrast;
|
||||||
uint8_t Language;
|
char Locale[LOCALE_STRLEN + 1];
|
||||||
struct {
|
struct {
|
||||||
uint32_t Duration;
|
uint32_t Duration;
|
||||||
uint8_t Mode;
|
uint8_t Mode;
|
||||||
@ -178,86 +159,38 @@ struct CONFIG_T {
|
|||||||
uint8_t Brightness;
|
uint8_t Brightness;
|
||||||
} Led_Single[PINMAPPING_LED_COUNT];
|
} Led_Single[PINMAPPING_LED_COUNT];
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
bool UpdatesOnly;
|
|
||||||
} Vedirect;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
uint32_t Interval;
|
|
||||||
uint32_t Source;
|
|
||||||
char MqttTopicPowerMeter1[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
uint32_t SdmBaudrate;
|
|
||||||
uint32_t SdmAddress;
|
|
||||||
uint32_t HttpInterval;
|
|
||||||
bool HttpIndividualRequests;
|
|
||||||
POWERMETER_HTTP_PHASE_CONFIG_T Http_Phase[POWERMETER_MAX_PHASES];
|
|
||||||
} PowerMeter;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
bool SolarPassThroughEnabled;
|
|
||||||
uint8_t SolarPassThroughLosses;
|
|
||||||
uint8_t BatteryDrainStategy;
|
|
||||||
uint32_t Interval;
|
|
||||||
bool IsInverterBehindPowerMeter;
|
|
||||||
uint8_t InverterId;
|
|
||||||
uint8_t InverterChannelId;
|
|
||||||
int32_t TargetPowerConsumption;
|
|
||||||
int32_t TargetPowerConsumptionHysteresis;
|
|
||||||
int32_t LowerPowerLimit;
|
|
||||||
int32_t UpperPowerLimit;
|
|
||||||
uint32_t BatterySocStartThreshold;
|
|
||||||
uint32_t BatterySocStopThreshold;
|
|
||||||
float VoltageStartThreshold;
|
|
||||||
float VoltageStopThreshold;
|
|
||||||
float VoltageLoadCorrectionFactor;
|
|
||||||
int8_t RestartHour;
|
|
||||||
uint32_t FullSolarPassThroughSoc;
|
|
||||||
float FullSolarPassThroughStartVoltage;
|
|
||||||
float FullSolarPassThroughStopVoltage;
|
|
||||||
} PowerLimiter;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
uint8_t Provider;
|
|
||||||
uint8_t JkBmsInterface;
|
|
||||||
uint8_t JkBmsPollingInterval;
|
|
||||||
char MqttTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
} Battery;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
uint32_t CAN_Controller_Frequency;
|
|
||||||
bool Auto_Power_Enabled;
|
|
||||||
float Auto_Power_Voltage_Limit;
|
|
||||||
float Auto_Power_Enable_Voltage_Limit;
|
|
||||||
float Auto_Power_Lower_Power_Limit;
|
|
||||||
float Auto_Power_Upper_Power_Limit;
|
|
||||||
} Huawei;
|
|
||||||
|
|
||||||
|
|
||||||
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
||||||
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigurationClass {
|
class ConfigurationClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
bool read();
|
bool read();
|
||||||
bool write();
|
bool write();
|
||||||
void migrate();
|
void migrate();
|
||||||
CONFIG_T& get();
|
CONFIG_T const& get();
|
||||||
|
|
||||||
|
class WriteGuard {
|
||||||
|
public:
|
||||||
|
WriteGuard();
|
||||||
|
CONFIG_T& getConfig();
|
||||||
|
~WriteGuard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_lock<std::mutex> _lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
WriteGuard getWriteGuard();
|
||||||
|
|
||||||
INVERTER_CONFIG_T* getFreeInverterSlot();
|
INVERTER_CONFIG_T* getFreeInverterSlot();
|
||||||
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
||||||
|
void deleteInverterById(const uint8_t id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigurationClass Configuration;
|
extern ConfigurationClass Configuration;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
class DatastoreClass {
|
class DatastoreClass {
|
||||||
public:
|
public:
|
||||||
|
DatastoreClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
|
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
|
||||||
|
|||||||
@ -20,6 +20,7 @@ enum DisplayType_t {
|
|||||||
SSD1306,
|
SSD1306,
|
||||||
SH1106,
|
SH1106,
|
||||||
SSD1309,
|
SSD1309,
|
||||||
|
ST7567_GM12864I_59N,
|
||||||
DisplayType_Max,
|
DisplayType_Max,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ public:
|
|||||||
void setContrast(const uint8_t contrast);
|
void setContrast(const uint8_t contrast);
|
||||||
void setStatus(const bool turnOn);
|
void setStatus(const bool turnOn);
|
||||||
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
|
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
|
||||||
void setLanguage(const uint8_t language);
|
void setLocale(const String& locale);
|
||||||
void setDiagramMode(DiagramMode_t mode);
|
void setDiagramMode(DiagramMode_t mode);
|
||||||
void setStartupDisplay();
|
void setStartupDisplay();
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ private:
|
|||||||
|
|
||||||
DisplayType_t _display_type = DisplayType_t::None;
|
DisplayType_t _display_type = DisplayType_t::None;
|
||||||
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
||||||
uint8_t _display_language = DISPLAY_LANGUAGE;
|
String _display_language = DISPLAY_LOCALE;
|
||||||
uint8_t _mExtra;
|
uint8_t _mExtra;
|
||||||
const uint16_t _period = 1000;
|
const uint16_t _period = 1000;
|
||||||
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||||
@ -72,6 +73,15 @@ private:
|
|||||||
char _fmtText[32];
|
char _fmtText[32];
|
||||||
bool _isLarge = false;
|
bool _isLarge = false;
|
||||||
uint8_t _lineOffsets[5];
|
uint8_t _lineOffsets[5];
|
||||||
|
|
||||||
|
String _i18n_offline;
|
||||||
|
String _i18n_yield_today_kwh;
|
||||||
|
String _i18n_yield_today_wh;
|
||||||
|
String _i18n_date_format;
|
||||||
|
String _i18n_current_power_kw;
|
||||||
|
String _i18n_current_power_w;
|
||||||
|
String _i18n_yield_total_mwh;
|
||||||
|
String _i18n_yield_total_kwh;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DisplayGraphicClass Display;
|
extern DisplayGraphicClass Display;
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
|
|
||||||
class HttpPowerMeterClass {
|
|
||||||
public:
|
|
||||||
void init();
|
|
||||||
bool updateValues();
|
|
||||||
float getPower(int8_t phase);
|
|
||||||
char httpPowerMeterError[256];
|
|
||||||
bool queryPhase(int phase, const String& url, Auth authType, const char* username, const char* password,
|
|
||||||
const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
float power[POWERMETER_MAX_PHASES];
|
|
||||||
HTTPClient httpClient;
|
|
||||||
String httpResponse;
|
|
||||||
bool httpRequest(int phase, WiFiClient &wifiClient, const String& host, const String& uri, bool https, Auth authType, const char* username,
|
|
||||||
const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath);
|
|
||||||
void extractUrlComponents(const String& url, String& protocol, String& host, String& uri);
|
|
||||||
String extractParam(String& authReq, const String& param, const char delimit);
|
|
||||||
String getcNonce(const int len);
|
|
||||||
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter);
|
|
||||||
bool tryGetFloatValueForPhase(int phase, int httpCode, const char* jsonPath);
|
|
||||||
void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue);
|
|
||||||
String sha256(const String& data);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern HttpPowerMeterClass HttpPowerMeter;
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "SPI.h"
|
|
||||||
#include <mcp_can.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_MISO
|
|
||||||
#define HUAWEI_PIN_MISO 12
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_MOSI
|
|
||||||
#define HUAWEI_PIN_MOSI 13
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_SCLK
|
|
||||||
#define HUAWEI_PIN_SCLK 26
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_IRQ
|
|
||||||
#define HUAWEI_PIN_IRQ 25
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_CS
|
|
||||||
#define HUAWEI_PIN_CS 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_POWER
|
|
||||||
#define HUAWEI_PIN_POWER 33
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HUAWEI_MINIMAL_OFFLINE_VOLTAGE 48
|
|
||||||
#define HUAWEI_MINIMAL_ONLINE_VOLTAGE 42
|
|
||||||
|
|
||||||
#define MAX_CURRENT_MULTIPLIER 20
|
|
||||||
|
|
||||||
// Index values for rec_values array
|
|
||||||
#define HUAWEI_INPUT_POWER_IDX 0
|
|
||||||
#define HUAWEI_INPUT_FREQ_IDX 1
|
|
||||||
#define HUAWEI_INPUT_CURRENT_IDX 2
|
|
||||||
#define HUAWEI_OUTPUT_POWER_IDX 3
|
|
||||||
#define HUAWEI_EFFICIENCY_IDX 4
|
|
||||||
#define HUAWEI_OUTPUT_VOLTAGE_IDX 5
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT_MAX_IDX 6
|
|
||||||
#define HUAWEI_INPUT_VOLTAGE_IDX 7
|
|
||||||
#define HUAWEI_OUTPUT_TEMPERATURE_IDX 8
|
|
||||||
#define HUAWEI_INPUT_TEMPERATURE_IDX 9
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT_IDX 10
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT1_IDX 11
|
|
||||||
|
|
||||||
// Defines and index values for tx_values array
|
|
||||||
#define HUAWEI_OFFLINE_VOLTAGE 0x01
|
|
||||||
#define HUAWEI_ONLINE_VOLTAGE 0x00
|
|
||||||
#define HUAWEI_OFFLINE_CURRENT 0x04
|
|
||||||
#define HUAWEI_ONLINE_CURRENT 0x03
|
|
||||||
|
|
||||||
// Modes of operation
|
|
||||||
#define HUAWEI_MODE_OFF 0
|
|
||||||
#define HUAWEI_MODE_ON 1
|
|
||||||
#define HUAWEI_MODE_AUTO_EXT 2
|
|
||||||
#define HUAWEI_MODE_AUTO_INT 3
|
|
||||||
|
|
||||||
// Error codes
|
|
||||||
#define HUAWEI_ERROR_CODE_RX 0x01
|
|
||||||
#define HUAWEI_ERROR_CODE_TX 0x02
|
|
||||||
|
|
||||||
// Wait time/current before shuting down the PSU / charger
|
|
||||||
// This is set to allow the fan to run for some time
|
|
||||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_DELAY 60000
|
|
||||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT 0.75
|
|
||||||
|
|
||||||
// Updateinterval used to request new values from the PSU
|
|
||||||
#define HUAWEI_DATA_REQUEST_INTERVAL_MS 2500
|
|
||||||
|
|
||||||
typedef struct RectifierParameters {
|
|
||||||
float input_voltage;
|
|
||||||
float input_frequency;
|
|
||||||
float input_current;
|
|
||||||
float input_power;
|
|
||||||
float input_temp;
|
|
||||||
float efficiency;
|
|
||||||
float output_voltage;
|
|
||||||
float output_current;
|
|
||||||
float max_output_current;
|
|
||||||
float output_power;
|
|
||||||
float output_temp;
|
|
||||||
float amp_hour;
|
|
||||||
} RectifierParameters_t;
|
|
||||||
|
|
||||||
class HuaweiCanCommClass {
|
|
||||||
public:
|
|
||||||
bool init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk,
|
|
||||||
uint8_t huawei_irq, uint8_t huawei_cs, uint32_t frequency);
|
|
||||||
void loop();
|
|
||||||
bool gotNewRxDataFrame(bool clear);
|
|
||||||
uint8_t getErrorCode(bool clear);
|
|
||||||
uint32_t getParameterValue(uint8_t parameter);
|
|
||||||
void setParameterValue(uint16_t in, uint8_t parameterType);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void sendRequest();
|
|
||||||
|
|
||||||
SPIClass *SPI;
|
|
||||||
MCP_CAN *_CAN;
|
|
||||||
uint8_t _huaweiIrq; // IRQ pin
|
|
||||||
uint32_t _nextRequestMillis = 0; // When to send next data request to PSU
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
|
||||||
|
|
||||||
uint32_t _recValues[12];
|
|
||||||
uint16_t _txValues[5];
|
|
||||||
bool _hasNewTxValue[5];
|
|
||||||
|
|
||||||
uint8_t _errorCode;
|
|
||||||
bool _completeUpdateReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HuaweiCanClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler, uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
|
|
||||||
void updateSettings(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
|
|
||||||
void setValue(float in, uint8_t parameterType);
|
|
||||||
void setMode(uint8_t mode);
|
|
||||||
|
|
||||||
RectifierParameters_t * get();
|
|
||||||
uint32_t getLastUpdate();
|
|
||||||
bool getAutoPowerStatus();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void processReceivedParameters();
|
|
||||||
void _setValue(float in, uint8_t parameterType);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
TaskHandle_t _HuaweiCanCommunicationTaskHdl = NULL;
|
|
||||||
bool _initialized = false;
|
|
||||||
uint8_t _huaweiPower; // Power pin
|
|
||||||
uint8_t _mode = HUAWEI_MODE_AUTO_EXT;
|
|
||||||
|
|
||||||
RectifierParameters_t _rp;
|
|
||||||
|
|
||||||
uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU
|
|
||||||
uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps
|
|
||||||
uint32_t _nextAutoModePeriodicIntMillis; // When to set the next output voltage in automatic mode
|
|
||||||
uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last seen power meter value
|
|
||||||
uint32_t _autoModeBlockedTillMillis = 0; // Timestamp to block running auto mode for some time
|
|
||||||
|
|
||||||
uint8_t _autoPowerEnabledCounter = 0;
|
|
||||||
bool _autoPowerEnabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern HuaweiCanClass HuaweiCan;
|
|
||||||
extern HuaweiCanCommClass HuaweiCanComm;
|
|
||||||
35
include/I18n.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <WString.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
struct LanguageInfo_t {
|
||||||
|
String code;
|
||||||
|
String name;
|
||||||
|
String filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
class I18nClass {
|
||||||
|
public:
|
||||||
|
I18nClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
std::list<LanguageInfo_t> getAvailableLanguages();
|
||||||
|
String getFilenameByLocale(const String& locale) const;
|
||||||
|
void readDisplayStrings(
|
||||||
|
const String& locale,
|
||||||
|
String& date_format,
|
||||||
|
String& offline,
|
||||||
|
String& power_w, String& power_kw,
|
||||||
|
String& yield_today_wh, String& yield_today_kwh,
|
||||||
|
String& yield_total_kwh, String& yield_total_mwh);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readLangPacks();
|
||||||
|
void readConfig(String file);
|
||||||
|
|
||||||
|
std::list<LanguageInfo_t> _availLanguages;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern I18nClass I18n;
|
||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
class InverterSettingsClass {
|
class InverterSettingsClass {
|
||||||
public:
|
public:
|
||||||
|
InverterSettingsClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
#include "JkBmsSerialMessage.h"
|
|
||||||
|
|
||||||
class DataPointContainer;
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
class Controller : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
Controller() = default;
|
|
||||||
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final;
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum class Status : unsigned {
|
|
||||||
Initializing,
|
|
||||||
Timeout,
|
|
||||||
WaitingForPollInterval,
|
|
||||||
HwSerialNotAvailableForWrite,
|
|
||||||
BusyReading,
|
|
||||||
RequestSent,
|
|
||||||
FrameCompleted
|
|
||||||
};
|
|
||||||
|
|
||||||
frozen::string const& getStatusText(Status status);
|
|
||||||
void announceStatus(Status status);
|
|
||||||
void sendRequest(uint8_t pollInterval);
|
|
||||||
void rxData(uint8_t inbyte);
|
|
||||||
void reset();
|
|
||||||
void frameComplete();
|
|
||||||
void processDataPoints(DataPointContainer const& dataPoints);
|
|
||||||
|
|
||||||
enum class Interface : unsigned {
|
|
||||||
Invalid,
|
|
||||||
Uart,
|
|
||||||
Transceiver
|
|
||||||
};
|
|
||||||
|
|
||||||
Interface getInterface() const;
|
|
||||||
|
|
||||||
enum class ReadState : unsigned {
|
|
||||||
Idle,
|
|
||||||
WaitingForFrameStart,
|
|
||||||
FrameStartReceived,
|
|
||||||
StartMarkerReceived,
|
|
||||||
FrameLengthMsbReceived,
|
|
||||||
ReadingFrame
|
|
||||||
};
|
|
||||||
ReadState _readState;
|
|
||||||
void setReadState(ReadState state) {
|
|
||||||
_readState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
int8_t _rxEnablePin = -1;
|
|
||||||
int8_t _txEnablePin = -1;
|
|
||||||
Status _lastStatus = Status::Initializing;
|
|
||||||
uint32_t _lastStatusPrinted = 0;
|
|
||||||
uint32_t _lastRequest = 0;
|
|
||||||
uint16_t _frameLength = 0;
|
|
||||||
uint8_t _protocolVersion = -1;
|
|
||||||
SerialResponse::tData _buffer = {};
|
|
||||||
std::shared_ptr<JkBmsBatteryStats> _stats =
|
|
||||||
std::make_shared<JkBmsBatteryStats>();
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -1,304 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <variant>
|
|
||||||
#include <frozen/map.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
#define ALARM_BITS(fnc) \
|
|
||||||
fnc(LowCapacity, (1<<0)) \
|
|
||||||
fnc(BmsOvertemperature, (1<<1)) \
|
|
||||||
fnc(ChargingOvervoltage, (1<<2)) \
|
|
||||||
fnc(DischargeUndervoltage, (1<<3)) \
|
|
||||||
fnc(BatteryOvertemperature, (1<<4)) \
|
|
||||||
fnc(ChargingOvercurrent, (1<<5)) \
|
|
||||||
fnc(DischargeOvercurrent, (1<<6)) \
|
|
||||||
fnc(CellVoltageDifference, (1<<7)) \
|
|
||||||
fnc(BatteryBoxOvertemperature, (1<<8)) \
|
|
||||||
fnc(BatteryUndertemperature, (1<<9)) \
|
|
||||||
fnc(CellOvervoltage, (1<<10)) \
|
|
||||||
fnc(CellUndervoltage, (1<<11)) \
|
|
||||||
fnc(AProtect, (1<<12)) \
|
|
||||||
fnc(BProtect, (1<<13)) \
|
|
||||||
fnc(Reserved1, (1<<14)) \
|
|
||||||
fnc(Reserved2, (1<<15))
|
|
||||||
|
|
||||||
enum class AlarmBits : uint16_t {
|
|
||||||
#define ALARM_ENUM(name, value) name = value,
|
|
||||||
ALARM_BITS(ALARM_ENUM)
|
|
||||||
#undef ALARM_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
static const frozen::map<AlarmBits, frozen::string, 16> AlarmBitTexts = {
|
|
||||||
#define ALARM_TEXT(name, value) { AlarmBits::name, #name },
|
|
||||||
ALARM_BITS(ALARM_TEXT)
|
|
||||||
#undef ALARM_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
#define STATUS_BITS(fnc) \
|
|
||||||
fnc(ChargingActive, (1<<0)) \
|
|
||||||
fnc(DischargingActive, (1<<1)) \
|
|
||||||
fnc(BalancingActive, (1<<2)) \
|
|
||||||
fnc(BatteryOnline, (1<<3))
|
|
||||||
|
|
||||||
enum class StatusBits : uint16_t {
|
|
||||||
#define STATUS_ENUM(name, value) name = value,
|
|
||||||
STATUS_BITS(STATUS_ENUM)
|
|
||||||
#undef STATUS_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
static const frozen::map<StatusBits, frozen::string, 4> StatusBitTexts = {
|
|
||||||
#define STATUS_TEXT(name, value) { StatusBits::name, #name },
|
|
||||||
STATUS_BITS(STATUS_TEXT)
|
|
||||||
#undef STATUS_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DataPointLabel : uint8_t {
|
|
||||||
CellsMilliVolt = 0x79,
|
|
||||||
BmsTempCelsius = 0x80,
|
|
||||||
BatteryTempOneCelsius = 0x81,
|
|
||||||
BatteryTempTwoCelsius = 0x82,
|
|
||||||
BatteryVoltageMilliVolt = 0x83,
|
|
||||||
BatteryCurrentMilliAmps = 0x84,
|
|
||||||
BatterySoCPercent = 0x85,
|
|
||||||
BatteryTemperatureSensorAmount = 0x86,
|
|
||||||
BatteryCycles = 0x87,
|
|
||||||
BatteryCycleCapacity = 0x89,
|
|
||||||
BatteryCellAmount = 0x8a,
|
|
||||||
AlarmsBitmask = 0x8b,
|
|
||||||
StatusBitmask = 0x8c,
|
|
||||||
TotalOvervoltageThresholdMilliVolt = 0x8e,
|
|
||||||
TotalUndervoltageThresholdMilliVolt = 0x8f,
|
|
||||||
CellOvervoltageThresholdMilliVolt = 0x90,
|
|
||||||
CellOvervoltageRecoveryMilliVolt = 0x91,
|
|
||||||
CellOvervoltageProtectionDelaySeconds = 0x92,
|
|
||||||
CellUndervoltageThresholdMilliVolt = 0x93,
|
|
||||||
CellUndervoltageRecoveryMilliVolt = 0x94,
|
|
||||||
CellUndervoltageProtectionDelaySeconds = 0x95,
|
|
||||||
CellVoltageDiffThresholdMilliVolt = 0x96,
|
|
||||||
DischargeOvercurrentThresholdAmperes = 0x97,
|
|
||||||
DischargeOvercurrentDelaySeconds = 0x98,
|
|
||||||
ChargeOvercurrentThresholdAmps = 0x99,
|
|
||||||
ChargeOvercurrentDelaySeconds = 0x9a,
|
|
||||||
BalanceCellVoltageThresholdMilliVolt = 0x9b,
|
|
||||||
BalanceVoltageDiffThresholdMilliVolt = 0x9c,
|
|
||||||
BalancingEnabled = 0x9d,
|
|
||||||
BmsTempProtectionThresholdCelsius = 0x9e,
|
|
||||||
BmsTempRecoveryThresholdCelsius = 0x9f,
|
|
||||||
BatteryTempProtectionThresholdCelsius = 0xa0,
|
|
||||||
BatteryTempRecoveryThresholdCelsius = 0xa1,
|
|
||||||
BatteryTempDiffThresholdCelsius = 0xa2,
|
|
||||||
ChargeHighTempThresholdCelsius = 0xa3,
|
|
||||||
DischargeHighTempThresholdCelsius = 0xa4,
|
|
||||||
ChargeLowTempThresholdCelsius = 0xa5,
|
|
||||||
ChargeLowTempRecoveryCelsius = 0xa6,
|
|
||||||
DischargeLowTempThresholdCelsius = 0xa7,
|
|
||||||
DischargeLowTempRecoveryCelsius = 0xa8,
|
|
||||||
CellAmountSetting = 0xa9,
|
|
||||||
BatteryCapacitySettingAmpHours = 0xaa,
|
|
||||||
BatteryChargeEnabled = 0xab,
|
|
||||||
BatteryDischargeEnabled = 0xac,
|
|
||||||
CurrentCalibrationMilliAmps = 0xad,
|
|
||||||
BmsAddress = 0xae,
|
|
||||||
BatteryType = 0xaf,
|
|
||||||
SleepWaitTime = 0xb0, // what's this?
|
|
||||||
LowCapacityAlarmThresholdPercent = 0xb1,
|
|
||||||
ModificationPassword = 0xb2,
|
|
||||||
DedicatedChargerSwitch = 0xb3, // what's this?
|
|
||||||
EquipmentId = 0xb4,
|
|
||||||
DateOfManufacturing = 0xb5,
|
|
||||||
BmsHourMeterMinutes = 0xb6,
|
|
||||||
BmsSoftwareVersion = 0xb7,
|
|
||||||
CurrentCalibration = 0xb8,
|
|
||||||
ActualBatteryCapacityAmpHours = 0xb9,
|
|
||||||
ProductId = 0xba,
|
|
||||||
ProtocolVersion = 0xc0
|
|
||||||
};
|
|
||||||
|
|
||||||
using tCells = std::map<uint8_t, uint16_t>;
|
|
||||||
|
|
||||||
template<DataPointLabel> struct DataPointLabelTraits;
|
|
||||||
|
|
||||||
#define LABEL_TRAIT(n, t, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
|
|
||||||
using type = t; \
|
|
||||||
static constexpr char const name[] = #n; \
|
|
||||||
static constexpr char const unit[] = u; \
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the types associated with the labels are the types for the respective data
|
|
||||||
* points in the JkBms::DataPoint class. they are *not* always equal to the
|
|
||||||
* type used in the serial message.
|
|
||||||
*
|
|
||||||
* it is unfortunate that we have to repeat all enum values here to define the
|
|
||||||
* traits. code generation could help here (labels are defined in a single
|
|
||||||
* source of truth and this code is generated -- no typing errors, etc.).
|
|
||||||
* however, the compiler will complain if an enum is misspelled or traits are
|
|
||||||
* defined for a removed enum, so we will notice. it will also complain when a
|
|
||||||
* trait is missing and if a data point for a label without traits is added to
|
|
||||||
* the DataPointContainer class, because the traits must be available then.
|
|
||||||
* even though this is tedious to maintain, human errors will be caught.
|
|
||||||
*/
|
|
||||||
LABEL_TRAIT(CellsMilliVolt, tCells, "mV");
|
|
||||||
LABEL_TRAIT(BmsTempCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempOneCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempTwoCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryVoltageMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(BatteryCurrentMilliAmps, int32_t, "mA");
|
|
||||||
LABEL_TRAIT(BatterySoCPercent, uint8_t, "%");
|
|
||||||
LABEL_TRAIT(BatteryTemperatureSensorAmount, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCycles, uint16_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCycleCapacity, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(BatteryCellAmount, uint16_t, "");
|
|
||||||
LABEL_TRAIT(AlarmsBitmask, uint16_t, "");
|
|
||||||
LABEL_TRAIT(StatusBitmask, uint16_t, "");
|
|
||||||
LABEL_TRAIT(TotalOvervoltageThresholdMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(TotalUndervoltageThresholdMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageRecoveryMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageProtectionDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(CellUndervoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellUndervoltageRecoveryMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellUndervoltageProtectionDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(CellVoltageDiffThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(DischargeOvercurrentThresholdAmperes, uint16_t, "A");
|
|
||||||
LABEL_TRAIT(DischargeOvercurrentDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(ChargeOvercurrentThresholdAmps, uint16_t, "A");
|
|
||||||
LABEL_TRAIT(ChargeOvercurrentDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(BalanceCellVoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(BalanceVoltageDiffThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(BalancingEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(BmsTempProtectionThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BmsTempRecoveryThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempProtectionThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempRecoveryThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempDiffThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeHighTempThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeHighTempThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeLowTempThresholdCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeLowTempRecoveryCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeLowTempThresholdCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeLowTempRecoveryCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(CellAmountSetting, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCapacitySettingAmpHours, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(BatteryChargeEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(BatteryDischargeEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(CurrentCalibrationMilliAmps, uint16_t, "mA");
|
|
||||||
LABEL_TRAIT(BmsAddress, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryType, uint8_t, "");
|
|
||||||
LABEL_TRAIT(SleepWaitTime, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(LowCapacityAlarmThresholdPercent, uint8_t, "%");
|
|
||||||
LABEL_TRAIT(ModificationPassword, std::string, "");
|
|
||||||
LABEL_TRAIT(DedicatedChargerSwitch, bool, "");
|
|
||||||
LABEL_TRAIT(EquipmentId, std::string, "");
|
|
||||||
LABEL_TRAIT(DateOfManufacturing, std::string, "");
|
|
||||||
LABEL_TRAIT(BmsHourMeterMinutes, uint32_t, "min");
|
|
||||||
LABEL_TRAIT(BmsSoftwareVersion, std::string, "");
|
|
||||||
LABEL_TRAIT(CurrentCalibration, bool, "");
|
|
||||||
LABEL_TRAIT(ActualBatteryCapacityAmpHours, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(ProductId, std::string, "");
|
|
||||||
LABEL_TRAIT(ProtocolVersion, uint8_t, "");
|
|
||||||
#undef LABEL_TRAIT
|
|
||||||
|
|
||||||
class DataPoint {
|
|
||||||
friend class DataPointContainer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using tValue = std::variant<bool, uint8_t, uint16_t, uint32_t,
|
|
||||||
int16_t, int32_t, std::string, tCells>;
|
|
||||||
|
|
||||||
DataPoint() = delete;
|
|
||||||
|
|
||||||
DataPoint(DataPoint const& other)
|
|
||||||
: _strLabel(other._strLabel)
|
|
||||||
, _strValue(other._strValue)
|
|
||||||
, _strUnit(other._strUnit)
|
|
||||||
, _value(other._value)
|
|
||||||
, _timestamp(other._timestamp) { }
|
|
||||||
|
|
||||||
DataPoint(std::string const& strLabel, std::string const& strValue,
|
|
||||||
std::string const& strUnit, tValue value, uint32_t timestamp)
|
|
||||||
: _strLabel(strLabel)
|
|
||||||
, _strValue(strValue)
|
|
||||||
, _strUnit(strUnit)
|
|
||||||
, _value(std::move(value))
|
|
||||||
, _timestamp(timestamp) { }
|
|
||||||
|
|
||||||
std::string const& getLabelText() const { return _strLabel; }
|
|
||||||
std::string const& getValueText() const { return _strValue; }
|
|
||||||
std::string const& getUnitText() const { return _strUnit; }
|
|
||||||
uint32_t getTimestamp() const { return _timestamp; }
|
|
||||||
|
|
||||||
bool operator==(DataPoint const& other) const {
|
|
||||||
return _value == other._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _strLabel;
|
|
||||||
std::string _strValue;
|
|
||||||
std::string _strUnit;
|
|
||||||
tValue _value;
|
|
||||||
uint32_t _timestamp;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> std::string dataPointValueToStr(T const& v);
|
|
||||||
|
|
||||||
class DataPointContainer {
|
|
||||||
public:
|
|
||||||
DataPointContainer() = default;
|
|
||||||
|
|
||||||
using Label = DataPointLabel;
|
|
||||||
template<Label L> using Traits = JkBms::DataPointLabelTraits<L>;
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
void add(typename Traits<L>::type val) {
|
|
||||||
_dataPoints.emplace(
|
|
||||||
L,
|
|
||||||
DataPoint(
|
|
||||||
Traits<L>::name,
|
|
||||||
dataPointValueToStr(val),
|
|
||||||
Traits<L>::unit,
|
|
||||||
DataPoint::tValue(std::move(val)),
|
|
||||||
millis()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure add() is only called with the type expected for the
|
|
||||||
// respective label, no implicit conversions allowed.
|
|
||||||
template<Label L, typename T>
|
|
||||||
void add(T) = delete;
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
std::optional<DataPoint const> getDataPointFor() const {
|
|
||||||
auto it = _dataPoints.find(L);
|
|
||||||
if (it == _dataPoints.end()) { return std::nullopt; }
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
std::optional<typename Traits<L>::type> get() const {
|
|
||||||
auto optionalDataPoint = getDataPointFor<L>();
|
|
||||||
if (!optionalDataPoint.has_value()) { return std::nullopt; }
|
|
||||||
return std::get<typename Traits<L>::type>(optionalDataPoint->_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
using tMap = std::unordered_map<Label, DataPoint const>;
|
|
||||||
tMap::const_iterator cbegin() const { return _dataPoints.cbegin(); }
|
|
||||||
tMap::const_iterator cend() const { return _dataPoints.cend(); }
|
|
||||||
|
|
||||||
// copy all data points from source into this instance, overwriting
|
|
||||||
// existing data points in this instance.
|
|
||||||
void updateFrom(DataPointContainer const& source);
|
|
||||||
|
|
||||||
private:
|
|
||||||
tMap _dataPoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include "JkBmsDataPoints.h"
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
class SerialMessage {
|
|
||||||
public:
|
|
||||||
using tData = std::vector<uint8_t>;
|
|
||||||
|
|
||||||
SerialMessage() = delete;
|
|
||||||
|
|
||||||
enum class Command : uint8_t {
|
|
||||||
Activate = 0x01,
|
|
||||||
Write = 0x02,
|
|
||||||
Read = 0x03,
|
|
||||||
Password = 0x05,
|
|
||||||
ReadAll = 0x06
|
|
||||||
};
|
|
||||||
|
|
||||||
Command getCommand() const { return static_cast<Command>(_raw[8]); }
|
|
||||||
|
|
||||||
enum class Source : uint8_t {
|
|
||||||
BMS = 0x00,
|
|
||||||
Bluetooth = 0x01,
|
|
||||||
GPS = 0x02,
|
|
||||||
Host = 0x03
|
|
||||||
};
|
|
||||||
Source getSource() const { return static_cast<Source>(_raw[9]); }
|
|
||||||
|
|
||||||
enum class Type : uint8_t {
|
|
||||||
Command = 0x00,
|
|
||||||
Response = 0x01,
|
|
||||||
Unsolicited = 0x02
|
|
||||||
};
|
|
||||||
Type getType() const { return static_cast<Type>(_raw[10]); }
|
|
||||||
|
|
||||||
// this does *not* include the two byte start marker
|
|
||||||
uint16_t getFrameLength() const { return get<uint16_t>(_raw.cbegin()+2); }
|
|
||||||
|
|
||||||
uint32_t getTerminalId() const { return get<uint32_t>(_raw.cbegin()+4); }
|
|
||||||
|
|
||||||
// there are 20 bytes of overhead. two of those are the start marker
|
|
||||||
// bytes, which are *not* counted by the frame length.
|
|
||||||
uint16_t getVariableFieldLength() const { return getFrameLength() - 18; }
|
|
||||||
|
|
||||||
// the upper byte of the 4-byte "record number" is reserved (for encryption)
|
|
||||||
uint32_t getSequence() const { return get<uint32_t>(_raw.cend()-9) >> 8; }
|
|
||||||
|
|
||||||
bool isValid() const;
|
|
||||||
|
|
||||||
uint8_t const* data() { return _raw.data(); }
|
|
||||||
size_t size() { return _raw.size(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template <typename... Args>
|
|
||||||
explicit SerialMessage(Args&&... args) : _raw(std::forward<Args>(args)...) { }
|
|
||||||
|
|
||||||
template<typename T, typename It> T get(It&& pos) const;
|
|
||||||
template<typename It> bool getBool(It&& pos) const;
|
|
||||||
template<typename It> int16_t getTemperature(It&& pos) const;
|
|
||||||
template<typename It> std::string getString(It&& pos, size_t len, bool replaceZeroes = false) const;
|
|
||||||
void processBatteryCurrent(tData::const_iterator& pos, uint8_t protocolVersion);
|
|
||||||
template<typename T> void set(tData::iterator const& pos, T val);
|
|
||||||
uint16_t calcChecksum() const;
|
|
||||||
void updateChecksum();
|
|
||||||
|
|
||||||
tData _raw;
|
|
||||||
JkBms::DataPointContainer _dp;
|
|
||||||
|
|
||||||
static constexpr uint16_t startMarker = 0x4e57;
|
|
||||||
static constexpr uint8_t endMarker = 0x68;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SerialResponse : public SerialMessage {
|
|
||||||
public:
|
|
||||||
using tData = SerialMessage::tData;
|
|
||||||
explicit SerialResponse(tData&& raw, uint8_t protocolVersion = -1);
|
|
||||||
|
|
||||||
DataPointContainer const& getDataPoints() const { return _dp; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class SerialCommand : public SerialMessage {
|
|
||||||
public:
|
|
||||||
using Command = SerialMessage::Command;
|
|
||||||
explicit SerialCommand(Command cmd);
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -2,16 +2,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AsyncWebSocket.h>
|
#include <AsyncWebSocket.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <Stream.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <Print.h>
|
|
||||||
#include <freertos/task.h>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
#define BUFFER_SIZE 500
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
class MessageOutputClass : public Print {
|
class MessageOutputClass : public Print {
|
||||||
public:
|
public:
|
||||||
|
MessageOutputClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
size_t write(uint8_t c) override;
|
size_t write(uint8_t c) override;
|
||||||
size_t write(const uint8_t* buffer, size_t size) override;
|
size_t write(const uint8_t* buffer, size_t size) override;
|
||||||
@ -22,19 +22,13 @@ private:
|
|||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
using message_t = std::vector<uint8_t>;
|
|
||||||
|
|
||||||
// we keep a buffer for every task and only write complete lines to the
|
|
||||||
// serial output and then move them to be pushed through the websocket.
|
|
||||||
// this way we prevent mangling of messages from different contexts.
|
|
||||||
std::unordered_map<TaskHandle_t, message_t> _task_messages;
|
|
||||||
std::queue<message_t> _lines;
|
|
||||||
|
|
||||||
AsyncWebSocket* _ws = nullptr;
|
AsyncWebSocket* _ws = nullptr;
|
||||||
|
char _buffer[BUFFER_SIZE];
|
||||||
|
uint16_t _buff_pos = 0;
|
||||||
|
uint32_t _lastSend = 0;
|
||||||
|
bool _forceSend = false;
|
||||||
|
|
||||||
std::mutex _msgLock;
|
std::mutex _msgLock;
|
||||||
|
|
||||||
void serialWrite(message_t const& m);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MessageOutputClass MessageOutput;
|
extern MessageOutputClass MessageOutput;
|
||||||
@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
|
|
||||||
class MqttBattery : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
MqttBattery() = default;
|
|
||||||
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final { return; } // this class is event-driven
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool _verboseLogging = false;
|
|
||||||
String _socTopic;
|
|
||||||
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
|
|
||||||
|
|
||||||
void onMqttMessage(espMqttClientTypes::MessageProperties const& properties,
|
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total);
|
|
||||||
};
|
|
||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
class MqttHandleDtuClass {
|
class MqttHandleDtuClass {
|
||||||
public:
|
public:
|
||||||
|
MqttHandleDtuClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -6,29 +6,42 @@
|
|||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {
|
enum DeviceClassType {
|
||||||
DEVICE_CLS_NONE = 0,
|
DEVICE_CLS_NONE = 0,
|
||||||
DEVICE_CLS_CURRENT,
|
DEVICE_CLS_CURRENT,
|
||||||
DEVICE_CLS_ENERGY,
|
DEVICE_CLS_ENERGY,
|
||||||
DEVICE_CLS_PWR,
|
DEVICE_CLS_PWR,
|
||||||
DEVICE_CLS_VOLTAGE,
|
DEVICE_CLS_VOLTAGE,
|
||||||
DEVICE_CLS_FREQ,
|
DEVICE_CLS_FREQ,
|
||||||
DEVICE_CLS_TEMP,
|
|
||||||
DEVICE_CLS_POWER_FACTOR,
|
DEVICE_CLS_POWER_FACTOR,
|
||||||
DEVICE_CLS_REACTIVE_POWER
|
DEVICE_CLS_REACTIVE_POWER,
|
||||||
|
DEVICE_CLS_CONNECTIVITY,
|
||||||
|
DEVICE_CLS_DURATION,
|
||||||
|
DEVICE_CLS_SIGNAL_STRENGTH,
|
||||||
|
DEVICE_CLS_TEMPERATURE,
|
||||||
|
DEVICE_CLS_RESTART
|
||||||
};
|
};
|
||||||
const char* const deviceClasses[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power" };
|
const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" };
|
||||||
enum {
|
|
||||||
|
enum StateClassType {
|
||||||
STATE_CLS_NONE = 0,
|
STATE_CLS_NONE = 0,
|
||||||
STATE_CLS_MEASUREMENT,
|
STATE_CLS_MEASUREMENT,
|
||||||
STATE_CLS_TOTAL_INCREASING
|
STATE_CLS_TOTAL_INCREASING
|
||||||
};
|
};
|
||||||
const char* const stateClasses[] = { 0, "measurement", "total_increasing" };
|
const char* const stateClass_name[] = { 0, "measurement", "total_increasing" };
|
||||||
|
|
||||||
|
enum CategoryType {
|
||||||
|
CATEGORY_NONE = 0,
|
||||||
|
CATEGORY_CONFIG,
|
||||||
|
CATEGORY_DIAGNOSTIC
|
||||||
|
};
|
||||||
|
const char* const category_name[] = { 0, "config", "diagnostic" };
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FieldId_t fieldId; // field id
|
FieldId_t fieldId; // field id
|
||||||
uint8_t deviceClsId; // device class
|
DeviceClassType deviceClsId; // device class
|
||||||
uint8_t stateClsId; // state class
|
StateClassType stateClsId; // state class
|
||||||
} byteAssign_fieldDeviceClass_t;
|
} byteAssign_fieldDeviceClass_t;
|
||||||
|
|
||||||
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
||||||
@ -41,7 +54,7 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
|||||||
{ FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT },
|
{ FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT },
|
{ FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT },
|
{ FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT },
|
{ FLD_T, DEVICE_CLS_TEMPERATURE, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT },
|
{ FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
{ FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
||||||
{ FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
{ FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
||||||
@ -51,24 +64,36 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
|||||||
|
|
||||||
class MqttHandleHassClass {
|
class MqttHandleHassClass {
|
||||||
public:
|
public:
|
||||||
|
MqttHandleHassClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
void publishConfig();
|
void publishConfig();
|
||||||
void forceUpdate();
|
void forceUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publish(const String& subtopic, const String& payload);
|
static void publish(const String& subtopic, const String& payload);
|
||||||
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
|
static void publish(const String& subtopic, const JsonDocument& doc);
|
||||||
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
|
|
||||||
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
|
||||||
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
|
||||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100);
|
|
||||||
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
|
||||||
|
|
||||||
static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
static void createDtuInfo(DynamicJsonDocument& doc);
|
|
||||||
|
|
||||||
static void createDeviceInfo(DynamicJsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
// Binary Sensor
|
||||||
|
static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
// Sensor
|
||||||
|
static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
||||||
|
static void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||||
|
static void createDtuInfo(JsonDocument& doc);
|
||||||
|
|
||||||
|
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
||||||
|
|
||||||
static String getDtuUniqueId();
|
static String getDtuUniqueId();
|
||||||
static String getDtuUrl();
|
static String getDtuUrl();
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <Huawei_can.h>
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class MqttHandleHuaweiClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
enum class Topic : unsigned {
|
|
||||||
LimitOnlineVoltage,
|
|
||||||
LimitOnlineCurrent,
|
|
||||||
LimitOfflineVoltage,
|
|
||||||
LimitOfflineCurrent,
|
|
||||||
Mode
|
|
||||||
};
|
|
||||||
|
|
||||||
void onMqttMessage(Topic t,
|
|
||||||
const espMqttClientTypes::MessageProperties& properties,
|
|
||||||
const char* topic, const uint8_t* payload, size_t len,
|
|
||||||
size_t index, size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
uint32_t _lastPublishStats;
|
|
||||||
uint32_t _lastPublish;
|
|
||||||
|
|
||||||
// MQTT callbacks to process updates on subscribed topics are executed in
|
|
||||||
// the MQTT thread's context. we use this queue to switch processing the
|
|
||||||
// user requests into the main loop's context (TaskScheduler context).
|
|
||||||
mutable std::mutex _mqttMutex;
|
|
||||||
std::deque<std::function<void()>> _mqttCallbacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleHuaweiClass MqttHandleHuawei;
|
|
||||||
@ -5,17 +5,22 @@
|
|||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
#include <frozen/map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
class MqttHandleInverterClass {
|
class MqttHandleInverterClass {
|
||||||
public:
|
public:
|
||||||
|
MqttHandleInverterClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
|
|
||||||
|
void subscribeTopics();
|
||||||
|
void unsubscribeTopics();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
@ -37,6 +42,29 @@ private:
|
|||||||
FLD_IRR,
|
FLD_IRR,
|
||||||
FLD_Q
|
FLD_Q
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Topic : unsigned {
|
||||||
|
LimitPersistentRelative,
|
||||||
|
LimitPersistentAbsolute,
|
||||||
|
LimitNonPersistentRelative,
|
||||||
|
LimitNonPersistentAbsolute,
|
||||||
|
Power,
|
||||||
|
Restart,
|
||||||
|
ResetRfStats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr frozen::string _cmdtopic = "+/cmd/";
|
||||||
|
static constexpr frozen::map<frozen::string, Topic, 7> _subscriptions = {
|
||||||
|
{ "limit_persistent_relative", Topic::LimitPersistentRelative },
|
||||||
|
{ "limit_persistent_absolute", Topic::LimitPersistentAbsolute },
|
||||||
|
{ "limit_nonpersistent_relative", Topic::LimitNonPersistentRelative },
|
||||||
|
{ "limit_nonpersistent_absolute", Topic::LimitNonPersistentAbsolute },
|
||||||
|
{ "power", Topic::Power },
|
||||||
|
{ "restart", Topic::Restart },
|
||||||
|
{ "reset_rf_stats", Topic::ResetRfStats },
|
||||||
|
};
|
||||||
|
|
||||||
|
void onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterClass MqttHandleInverter;
|
extern MqttHandleInverterClass MqttHandleInverter;
|
||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
class MqttHandleInverterTotalClass {
|
class MqttHandleInverterTotalClass {
|
||||||
public:
|
public:
|
||||||
|
MqttHandleInverterTotalClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class MqttHandlePowerLimiterClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void onCmdMode(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
uint32_t _lastPublishStats;
|
|
||||||
uint32_t _lastPublish;
|
|
||||||
|
|
||||||
// MQTT callbacks to process updates on subscribed topics are executed in
|
|
||||||
// the MQTT thread's context. we use this queue to switch processing the
|
|
||||||
// user requests into the main loop's context (TaskScheduler context).
|
|
||||||
mutable std::mutex _mqttMutex;
|
|
||||||
std::deque<std::function<void()>> _mqttCallbacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandlePowerLimiterClass MqttHandlePowerLimiter;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class MqttHandlePylontechHassClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void publishConfig();
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void publish(const String& subtopic, const String& payload);
|
|
||||||
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
|
|
||||||
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
|
|
||||||
void createDeviceInfo(JsonObject& object);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _wasConnected = false;
|
|
||||||
bool _updateForced = false;
|
|
||||||
String serial = "0001"; // pseudo-serial, can be replaced in future with real serialnumber
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandlePylontechHassClass MqttHandlePylontechHass;
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#ifndef VICTRON_PIN_RX
|
|
||||||
#define VICTRON_PIN_RX 22
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef VICTRON_PIN_TX
|
|
||||||
#define VICTRON_PIN_TX 21
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class MqttHandleVedirectClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void forceUpdate();
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
VeDirectMpptController::veMpptStruct _kvFrame{};
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
// point of time in millis() when updated values will be published
|
|
||||||
uint32_t _nextPublishUpdatesOnly = 0;
|
|
||||||
|
|
||||||
// point of time in millis() when all values will be published
|
|
||||||
uint32_t _nextPublishFull = 1;
|
|
||||||
|
|
||||||
bool _PublishFull;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleVedirectClass MqttHandleVedirect;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class MqttHandleVedirectHassClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void publishConfig();
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void publish(const String& subtopic, const String& payload);
|
|
||||||
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
|
|
||||||
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
|
|
||||||
void createDeviceInfo(JsonObject& object);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _wasConnected = false;
|
|
||||||
bool _updateForced = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleVedirectHassClass MqttHandleVedirectHass;
|
|
||||||
@ -11,7 +11,6 @@ class MqttSettingsClass {
|
|||||||
public:
|
public:
|
||||||
MqttSettingsClass();
|
MqttSettingsClass();
|
||||||
void init();
|
void init();
|
||||||
void loop();
|
|
||||||
void performReconnect();
|
void performReconnect();
|
||||||
bool getConnected();
|
bool getConnected();
|
||||||
void publish(const String& subtopic, const String& payload);
|
void publish(const String& subtopic, const String& payload);
|
||||||
@ -21,6 +20,7 @@ public:
|
|||||||
void unsubscribe(const String& topic);
|
void unsubscribe(const String& topic);
|
||||||
|
|
||||||
String getPrefix() const;
|
String getPrefix() const;
|
||||||
|
String getClientId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void NetworkEvent(network_event event);
|
void NetworkEvent(network_event event);
|
||||||
@ -38,7 +38,6 @@ private:
|
|||||||
Ticker _mqttReconnectTimer;
|
Ticker _mqttReconnectTimer;
|
||||||
MqttSubscribeParser _mqttSubscribeParser;
|
MqttSubscribeParser _mqttSubscribeParser;
|
||||||
std::mutex _clientLock;
|
std::mutex _clientLock;
|
||||||
bool _verboseLogging = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttSettingsClass MqttSettings;
|
extern MqttSettingsClass MqttSettings;
|
||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "W5500.h"
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
@ -23,18 +24,18 @@ enum class network_event {
|
|||||||
NETWORK_EVENT_MAX
|
NETWORK_EVENT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(network_event event)> NetworkEventCb;
|
typedef std::function<void(network_event event)> DtuNetworkEventCb;
|
||||||
|
|
||||||
typedef struct NetworkEventCbList {
|
typedef struct DtuNetworkEventCbList {
|
||||||
NetworkEventCb cb;
|
DtuNetworkEventCb cb;
|
||||||
network_event event;
|
network_event event;
|
||||||
|
|
||||||
NetworkEventCbList()
|
DtuNetworkEventCbList()
|
||||||
: cb(nullptr)
|
: cb(nullptr)
|
||||||
, event(network_event::NETWORK_UNKNOWN)
|
, event(network_event::NETWORK_UNKNOWN)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
} NetworkEventCbList_t;
|
} DtuNetworkEventCbList_t;
|
||||||
|
|
||||||
class NetworkSettingsClass {
|
class NetworkSettingsClass {
|
||||||
public:
|
public:
|
||||||
@ -53,7 +54,7 @@ public:
|
|||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
network_mode NetworkMode() const;
|
network_mode NetworkMode() const;
|
||||||
|
|
||||||
bool onEvent(NetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
|
bool onEvent(DtuNetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
|
||||||
void raiseEvent(const network_event event);
|
void raiseEvent(const network_event event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -62,7 +63,7 @@ private:
|
|||||||
void setStaticIp();
|
void setStaticIp();
|
||||||
void handleMDNS();
|
void handleMDNS();
|
||||||
void setupMode();
|
void setupMode();
|
||||||
void NetworkEvent(const WiFiEvent_t event);
|
void NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info);
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
@ -81,8 +82,9 @@ private:
|
|||||||
bool _dnsServerStatus = false;
|
bool _dnsServerStatus = false;
|
||||||
network_mode _networkMode = network_mode::Undefined;
|
network_mode _networkMode = network_mode::Undefined;
|
||||||
bool _ethConnected = false;
|
bool _ethConnected = false;
|
||||||
std::vector<NetworkEventCbList_t> _cbEventList;
|
std::vector<DtuNetworkEventCbList_t> _cbEventList;
|
||||||
bool _lastMdnsEnabled = false;
|
bool _lastMdnsEnabled = false;
|
||||||
|
std::unique_ptr<W5500> _w5500;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NetworkSettingsClass NetworkSettings;
|
extern NetworkSettingsClass NetworkSettings;
|
||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
struct PinMapping_t {
|
struct PinMapping_t {
|
||||||
char name[MAPPING_NAME_STRLEN + 1];
|
char name[MAPPING_NAME_STRLEN + 1];
|
||||||
|
|
||||||
int8_t nrf24_miso;
|
int8_t nrf24_miso;
|
||||||
int8_t nrf24_mosi;
|
int8_t nrf24_mosi;
|
||||||
int8_t nrf24_clk;
|
int8_t nrf24_clk;
|
||||||
@ -26,6 +27,14 @@ struct PinMapping_t {
|
|||||||
int8_t cmt_gpio3;
|
int8_t cmt_gpio3;
|
||||||
int8_t cmt_sdio;
|
int8_t cmt_sdio;
|
||||||
|
|
||||||
|
int8_t w5500_mosi;
|
||||||
|
int8_t w5500_miso;
|
||||||
|
int8_t w5500_sclk;
|
||||||
|
int8_t w5500_cs;
|
||||||
|
int8_t w5500_int;
|
||||||
|
int8_t w5500_rst;
|
||||||
|
|
||||||
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
int8_t eth_phy_addr;
|
int8_t eth_phy_addr;
|
||||||
bool eth_enabled;
|
bool eth_enabled;
|
||||||
int eth_power;
|
int eth_power;
|
||||||
@ -33,23 +42,14 @@ struct PinMapping_t {
|
|||||||
int eth_mdio;
|
int eth_mdio;
|
||||||
eth_phy_type_t eth_type;
|
eth_phy_type_t eth_type;
|
||||||
eth_clock_mode_t eth_clk_mode;
|
eth_clock_mode_t eth_clk_mode;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8_t display_type;
|
uint8_t display_type;
|
||||||
uint8_t display_data;
|
uint8_t display_data;
|
||||||
uint8_t display_clk;
|
uint8_t display_clk;
|
||||||
uint8_t display_cs;
|
uint8_t display_cs;
|
||||||
uint8_t display_reset;
|
uint8_t display_reset;
|
||||||
int8_t victron_tx;
|
|
||||||
int8_t victron_rx;
|
|
||||||
int8_t battery_rx;
|
|
||||||
int8_t battery_rxen;
|
|
||||||
int8_t battery_tx;
|
|
||||||
int8_t battery_txen;
|
|
||||||
int8_t huawei_miso;
|
|
||||||
int8_t huawei_mosi;
|
|
||||||
int8_t huawei_clk;
|
|
||||||
int8_t huawei_irq;
|
|
||||||
int8_t huawei_cs;
|
|
||||||
int8_t huawei_power;
|
|
||||||
int8_t led[PINMAPPING_LED_COUNT];
|
int8_t led[PINMAPPING_LED_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,13 +59,19 @@ public:
|
|||||||
bool init(const String& deviceMapping);
|
bool init(const String& deviceMapping);
|
||||||
PinMapping_t& get();
|
PinMapping_t& get();
|
||||||
|
|
||||||
|
bool isMappingSelected() const { return _mappingSelected; }
|
||||||
|
|
||||||
bool isValidNrf24Config() const;
|
bool isValidNrf24Config() const;
|
||||||
bool isValidCmt2300Config() const;
|
bool isValidCmt2300Config() const;
|
||||||
|
bool isValidW5500Config() const;
|
||||||
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
bool isValidEthConfig() const;
|
bool isValidEthConfig() const;
|
||||||
bool isValidHuaweiConfig() const;
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PinMapping_t _pinMapping;
|
PinMapping_t _pinMapping;
|
||||||
|
|
||||||
|
bool _mappingSelected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PinMappingClass PinMapping;
|
extern PinMappingClass PinMapping;
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Hoymiles.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
#define PL_UI_STATE_INACTIVE 0
|
|
||||||
#define PL_UI_STATE_CHARGING 1
|
|
||||||
#define PL_UI_STATE_USE_SOLAR_ONLY 2
|
|
||||||
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
|
|
||||||
|
|
||||||
#define PL_MODE_ENABLE_NORMAL_OP 0
|
|
||||||
#define PL_MODE_FULL_DISABLE 1
|
|
||||||
#define PL_MODE_SOLAR_PT_ONLY 2
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
EMPTY_WHEN_FULL= 0,
|
|
||||||
EMPTY_AT_NIGHT
|
|
||||||
} batDrainStrategy;
|
|
||||||
|
|
||||||
|
|
||||||
class PowerLimiterClass {
|
|
||||||
public:
|
|
||||||
enum class Status : unsigned {
|
|
||||||
Initializing,
|
|
||||||
DisabledByConfig,
|
|
||||||
DisabledByMqtt,
|
|
||||||
WaitingForValidTimestamp,
|
|
||||||
PowerMeterDisabled,
|
|
||||||
PowerMeterTimeout,
|
|
||||||
PowerMeterPending,
|
|
||||||
InverterInvalid,
|
|
||||||
InverterChanged,
|
|
||||||
InverterOffline,
|
|
||||||
InverterCommandsDisabled,
|
|
||||||
InverterLimitPending,
|
|
||||||
InverterPowerCmdPending,
|
|
||||||
InverterDevInfoPending,
|
|
||||||
InverterStatsPending,
|
|
||||||
UnconditionalSolarPassthrough,
|
|
||||||
NoVeDirect,
|
|
||||||
Settling,
|
|
||||||
Stable,
|
|
||||||
};
|
|
||||||
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
uint8_t getPowerLimiterState();
|
|
||||||
int32_t getLastRequestedPowerLimit();
|
|
||||||
|
|
||||||
enum class Mode : unsigned {
|
|
||||||
Normal = 0,
|
|
||||||
Disabled = 1,
|
|
||||||
UnconditionalFullSolarPassthrough = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
void setMode(Mode m) { _mode = m; }
|
|
||||||
Mode getMode() const { return _mode; }
|
|
||||||
void calcNextInverterRestart();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
int32_t _lastRequestedPowerLimit = 0;
|
|
||||||
uint32_t _lastPowerLimitMillis = 0;
|
|
||||||
uint32_t _shutdownTimeout = 0;
|
|
||||||
Status _lastStatus = Status::Initializing;
|
|
||||||
uint32_t _lastStatusPrinted = 0;
|
|
||||||
uint32_t _lastCalculation = 0;
|
|
||||||
static constexpr uint32_t _calculationBackoffMsDefault = 128;
|
|
||||||
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
|
|
||||||
Mode _mode = Mode::Normal;
|
|
||||||
std::shared_ptr<InverterAbstract> _inverter = nullptr;
|
|
||||||
bool _batteryDischargeEnabled = false;
|
|
||||||
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
|
|
||||||
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
|
|
||||||
bool _fullSolarPassThroughEnabled = false;
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
|
|
||||||
frozen::string const& getStatusText(Status status);
|
|
||||||
void announceStatus(Status status);
|
|
||||||
bool shutdown(Status status);
|
|
||||||
bool shutdown() { return shutdown(_lastStatus); }
|
|
||||||
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
|
|
||||||
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
|
||||||
bool canUseDirectSolarPower();
|
|
||||||
int32_t calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool solarPowerEnabled, bool batteryDischargeEnabled);
|
|
||||||
void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction);
|
|
||||||
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
|
|
||||||
int32_t getSolarChargePower();
|
|
||||||
float getLoadCorrectedVoltage();
|
|
||||||
bool testThreshold(float socThreshold, float voltThreshold,
|
|
||||||
std::function<bool(float, float)> compare);
|
|
||||||
bool isStartThresholdReached();
|
|
||||||
bool isStopThresholdReached();
|
|
||||||
bool isBelowStopThreshold();
|
|
||||||
bool useFullSolarPassthrough();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PowerLimiterClass PowerLimiter;
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include "SDM.h"
|
|
||||||
#include "sml.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#ifndef SDM_RX_PIN
|
|
||||||
#define SDM_RX_PIN 13
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SDM_TX_PIN
|
|
||||||
#define SDM_TX_PIN 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SML_RX_PIN
|
|
||||||
#define SML_RX_PIN 35
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const unsigned char OBIS[6];
|
|
||||||
void (*Fn)(double&);
|
|
||||||
float* Arg;
|
|
||||||
} OBISHandler;
|
|
||||||
|
|
||||||
class PowerMeterClass {
|
|
||||||
public:
|
|
||||||
enum SOURCE {
|
|
||||||
SOURCE_MQTT = 0,
|
|
||||||
SOURCE_SDM1PH = 1,
|
|
||||||
SOURCE_SDM3PH = 2,
|
|
||||||
SOURCE_HTTP = 3,
|
|
||||||
SOURCE_SML = 4
|
|
||||||
};
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
float getPowerTotal(bool forceUpdate = true);
|
|
||||||
uint32_t getLastPowerMeterUpdate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void mqtt();
|
|
||||||
|
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties,
|
|
||||||
const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
uint32_t _lastPowerMeterCheck;
|
|
||||||
// Used in Power limiter for safety check
|
|
||||||
uint32_t _lastPowerMeterUpdate;
|
|
||||||
|
|
||||||
float _powerMeter1Power = 0.0;
|
|
||||||
float _powerMeter2Power = 0.0;
|
|
||||||
float _powerMeter3Power = 0.0;
|
|
||||||
float _powerMeter1Voltage = 0.0;
|
|
||||||
float _powerMeter2Voltage = 0.0;
|
|
||||||
float _powerMeter3Voltage = 0.0;
|
|
||||||
float _powerMeterImport = 0.0;
|
|
||||||
float _powerMeterExport = 0.0;
|
|
||||||
|
|
||||||
std::map<String, float*> _mqttSubscriptions;
|
|
||||||
|
|
||||||
void readPowerMeter();
|
|
||||||
|
|
||||||
bool smlReadLoop();
|
|
||||||
const std::list<OBISHandler> smlHandlerList{
|
|
||||||
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power},
|
|
||||||
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
|
|
||||||
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PowerMeterClass PowerMeter;
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "Battery.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <driver/twai.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class PylontechCanReceiver : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final;
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16_t readUnsignedInt16(uint8_t *data);
|
|
||||||
int16_t readSignedInt16(uint8_t *data);
|
|
||||||
float scaleValue(int16_t value, float factor);
|
|
||||||
bool getBit(uint8_t value, uint8_t bit);
|
|
||||||
|
|
||||||
void dummyData();
|
|
||||||
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
std::shared_ptr<PylontechBatteryStats> _stats =
|
|
||||||
std::make_shared<PylontechBatteryStats>();
|
|
||||||
};
|
|
||||||
18
include/RestartHelper.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
class RestartHelperClass {
|
||||||
|
public:
|
||||||
|
RestartHelperClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
void triggerRestart();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
Task _rebootTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern RestartHelperClass RestartHelper;
|
||||||
@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
@ -9,6 +10,8 @@ public:
|
|||||||
static uint32_t getChipId();
|
static uint32_t getChipId();
|
||||||
static uint64_t generateDtuSerial();
|
static uint64_t generateDtuSerial();
|
||||||
static int getTimezoneOffset();
|
static int getTimezoneOffset();
|
||||||
static void restartDtu();
|
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||||
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
|
static void removeAllFiles();
|
||||||
|
static String generateMd5FromFile(String file);
|
||||||
|
static void skipBom(File& f);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class VictronMpptClass {
|
|
||||||
public:
|
|
||||||
VictronMpptClass() = default;
|
|
||||||
~VictronMpptClass() = default;
|
|
||||||
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
bool isDataValid() const;
|
|
||||||
|
|
||||||
// returns the data age of all controllers,
|
|
||||||
// i.e, the youngest data's age is returned.
|
|
||||||
uint32_t getDataAgeMillis() const;
|
|
||||||
|
|
||||||
VeDirectMpptController::spData_t getData(size_t idx = 0) const;
|
|
||||||
|
|
||||||
// total output of all MPPT charge controllers in Watts
|
|
||||||
int32_t getPowerOutputWatts() const;
|
|
||||||
|
|
||||||
// total panel input power of all MPPT charge controllers in Watts
|
|
||||||
int32_t getPanelPowerWatts() const;
|
|
||||||
|
|
||||||
// sum of total yield of all MPPT charge controllers in kWh
|
|
||||||
double getYieldTotal() const;
|
|
||||||
|
|
||||||
// sum of today's yield of all MPPT charge controllers in kWh
|
|
||||||
double getYieldDay() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
VictronMpptClass(VictronMpptClass const& other) = delete;
|
|
||||||
VictronMpptClass(VictronMpptClass&& other) = delete;
|
|
||||||
VictronMpptClass& operator=(VictronMpptClass const& other) = delete;
|
|
||||||
VictronMpptClass& operator=(VictronMpptClass&& other) = delete;
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
using controller_t = std::unique_ptr<VeDirectMpptController>;
|
|
||||||
std::vector<controller_t> _controllers;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern VictronMpptClass VictronMppt;
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
|
|
||||||
class VictronSmartShunt : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final { }
|
|
||||||
void loop() final;
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t _lastUpdate = 0;
|
|
||||||
std::shared_ptr<VictronSmartShuntStats> _stats =
|
|
||||||
std::make_shared<VictronSmartShuntStats>();
|
|
||||||
};
|
|
||||||
29
include/W5500.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <esp_eth.h> // required for esp_eth_handle_t
|
||||||
|
#include <esp_netif.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class W5500 {
|
||||||
|
private:
|
||||||
|
explicit W5500(spi_device_handle_t spi, gpio_num_t pin_int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
W5500(const W5500&) = delete;
|
||||||
|
W5500& operator=(const W5500&) = delete;
|
||||||
|
~W5500();
|
||||||
|
|
||||||
|
static std::unique_ptr<W5500> setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst);
|
||||||
|
String macAddress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool connection_check_spi(spi_device_handle_t spi);
|
||||||
|
static bool connection_check_interrupt(gpio_num_t pin_int);
|
||||||
|
|
||||||
|
esp_eth_handle_t eth_handle;
|
||||||
|
esp_netif_t* eth_netif;
|
||||||
|
};
|
||||||
@ -1,15 +1,15 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "WebApi_battery.h"
|
|
||||||
#include "WebApi_config.h"
|
|
||||||
#include "WebApi_device.h"
|
#include "WebApi_device.h"
|
||||||
#include "WebApi_devinfo.h"
|
#include "WebApi_devinfo.h"
|
||||||
#include "WebApi_dtu.h"
|
#include "WebApi_dtu.h"
|
||||||
#include "WebApi_errors.h"
|
#include "WebApi_errors.h"
|
||||||
#include "WebApi_eventlog.h"
|
#include "WebApi_eventlog.h"
|
||||||
|
#include "WebApi_file.h"
|
||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
#include "WebApi_gridprofile.h"
|
#include "WebApi_gridprofile.h"
|
||||||
|
#include "WebApi_i18n.h"
|
||||||
#include "WebApi_inverter.h"
|
#include "WebApi_inverter.h"
|
||||||
#include "WebApi_limit.h"
|
#include "WebApi_limit.h"
|
||||||
#include "WebApi_maintenance.h"
|
#include "WebApi_maintenance.h"
|
||||||
@ -17,19 +17,13 @@
|
|||||||
#include "WebApi_network.h"
|
#include "WebApi_network.h"
|
||||||
#include "WebApi_ntp.h"
|
#include "WebApi_ntp.h"
|
||||||
#include "WebApi_power.h"
|
#include "WebApi_power.h"
|
||||||
#include "WebApi_powermeter.h"
|
|
||||||
#include "WebApi_powerlimiter.h"
|
|
||||||
#include "WebApi_prometheus.h"
|
#include "WebApi_prometheus.h"
|
||||||
#include "WebApi_security.h"
|
#include "WebApi_security.h"
|
||||||
#include "WebApi_sysstatus.h"
|
#include "WebApi_sysstatus.h"
|
||||||
#include "WebApi_webapp.h"
|
#include "WebApi_webapp.h"
|
||||||
#include "WebApi_ws_console.h"
|
#include "WebApi_ws_console.h"
|
||||||
#include "WebApi_ws_live.h"
|
#include "WebApi_ws_live.h"
|
||||||
#include "WebApi_ws_vedirect_live.h"
|
#include <AsyncJson.h>
|
||||||
#include "WebApi_vedirect.h"
|
|
||||||
#include "WebApi_ws_Huawei.h"
|
|
||||||
#include "WebApi_Huawei.h"
|
|
||||||
#include "WebApi_ws_battery.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
@ -37,6 +31,7 @@ class WebApiClass {
|
|||||||
public:
|
public:
|
||||||
WebApiClass();
|
WebApiClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
void reload();
|
||||||
|
|
||||||
static bool checkCredentials(AsyncWebServerRequest* request);
|
static bool checkCredentials(AsyncWebServerRequest* request);
|
||||||
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
|
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
|
||||||
@ -45,21 +40,21 @@ public:
|
|||||||
|
|
||||||
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
|
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
|
||||||
|
|
||||||
|
static bool parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document);
|
||||||
|
static uint64_t parseSerialFromRequest(AsyncWebServerRequest* request, String param_name = "inv");
|
||||||
|
static bool sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
AsyncWebServer _server;
|
AsyncWebServer _server;
|
||||||
|
|
||||||
WebApiBatteryClass _webApiBattery;
|
|
||||||
WebApiConfigClass _webApiConfig;
|
|
||||||
WebApiDeviceClass _webApiDevice;
|
WebApiDeviceClass _webApiDevice;
|
||||||
WebApiDevInfoClass _webApiDevInfo;
|
WebApiDevInfoClass _webApiDevInfo;
|
||||||
WebApiDtuClass _webApiDtu;
|
WebApiDtuClass _webApiDtu;
|
||||||
WebApiEventlogClass _webApiEventlog;
|
WebApiEventlogClass _webApiEventlog;
|
||||||
|
WebApiFileClass _webApiFile;
|
||||||
WebApiFirmwareClass _webApiFirmware;
|
WebApiFirmwareClass _webApiFirmware;
|
||||||
WebApiGridProfileClass _webApiGridprofile;
|
WebApiGridProfileClass _webApiGridprofile;
|
||||||
|
WebApiI18nClass _webApiI18n;
|
||||||
WebApiInverterClass _webApiInverter;
|
WebApiInverterClass _webApiInverter;
|
||||||
WebApiLimitClass _webApiLimit;
|
WebApiLimitClass _webApiLimit;
|
||||||
WebApiMaintenanceClass _webApiMaintenance;
|
WebApiMaintenanceClass _webApiMaintenance;
|
||||||
@ -67,19 +62,12 @@ private:
|
|||||||
WebApiNetworkClass _webApiNetwork;
|
WebApiNetworkClass _webApiNetwork;
|
||||||
WebApiNtpClass _webApiNtp;
|
WebApiNtpClass _webApiNtp;
|
||||||
WebApiPowerClass _webApiPower;
|
WebApiPowerClass _webApiPower;
|
||||||
WebApiPowerMeterClass _webApiPowerMeter;
|
|
||||||
WebApiPowerLimiterClass _webApiPowerLimiter;
|
|
||||||
WebApiPrometheusClass _webApiPrometheus;
|
WebApiPrometheusClass _webApiPrometheus;
|
||||||
WebApiSecurityClass _webApiSecurity;
|
WebApiSecurityClass _webApiSecurity;
|
||||||
WebApiSysstatusClass _webApiSysstatus;
|
WebApiSysstatusClass _webApiSysstatus;
|
||||||
WebApiWebappClass _webApiWebapp;
|
WebApiWebappClass _webApiWebapp;
|
||||||
WebApiWsConsoleClass _webApiWsConsole;
|
WebApiWsConsoleClass _webApiWsConsole;
|
||||||
WebApiWsLiveClass _webApiWsLive;
|
WebApiWsLiveClass _webApiWsLive;
|
||||||
WebApiWsVedirectLiveClass _webApiWsVedirectLive;
|
|
||||||
WebApiVedirectClass _webApiVedirect;
|
|
||||||
WebApiHuaweiClass _webApiHuaweiClass;
|
|
||||||
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
|
|
||||||
WebApiWsBatteryLiveClass _webApiWsBatteryLive;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WebApiClass WebApi;
|
extern WebApiClass WebApi;
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <AsyncJson.h>
|
|
||||||
|
|
||||||
class WebApiHuaweiClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
void getJsonData(JsonVariant& root);
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
void onPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
|
|
||||||
class WebApiBatteryClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
class WebApiConfigClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onConfigGet(AsyncWebServerRequest* request);
|
|
||||||
void onConfigDelete(AsyncWebServerRequest* request);
|
|
||||||
void onConfigListGet(AsyncWebServerRequest* request);
|
|
||||||
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
|
||||||
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDeviceClass {
|
class WebApiDeviceClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDevInfoClass {
|
class WebApiDevInfoClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDevInfoStatus(AsyncWebServerRequest* request);
|
void onDevInfoStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,16 +2,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDtuClass {
|
class WebApiDtuClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
WebApiDtuClass();
|
||||||
void loop();
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDtuAdminGet(AsyncWebServerRequest* request);
|
void onDtuAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDtuAdminPost(AsyncWebServerRequest* request);
|
void onDtuAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
Task _applyDataTask;
|
||||||
bool _performReload = false;
|
void applyDataTaskCb();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,10 +5,11 @@ enum WebApiError {
|
|||||||
GenericBase = 1000,
|
GenericBase = 1000,
|
||||||
GenericSuccess,
|
GenericSuccess,
|
||||||
GenericNoValueFound,
|
GenericNoValueFound,
|
||||||
GenericDataTooLarge,
|
GenericDataTooLarge, // not used anymore
|
||||||
GenericParseError,
|
GenericParseError,
|
||||||
GenericValueMissing,
|
GenericValueMissing,
|
||||||
GenericWriteFailed,
|
GenericWriteFailed,
|
||||||
|
GenericInternalServerError,
|
||||||
|
|
||||||
DtuBase = 2000,
|
DtuBase = 2000,
|
||||||
DtuSerialZero,
|
DtuSerialZero,
|
||||||
@ -17,9 +18,10 @@ enum WebApiError {
|
|||||||
DtuInvalidCmtFrequency,
|
DtuInvalidCmtFrequency,
|
||||||
DtuInvalidCmtCountry,
|
DtuInvalidCmtCountry,
|
||||||
|
|
||||||
ConfigBase = 3000,
|
FileBase = 3000,
|
||||||
ConfigNotDeleted,
|
FileNotDeleted,
|
||||||
ConfigSuccess,
|
FileSuccess,
|
||||||
|
FileDeleteSuccess,
|
||||||
|
|
||||||
InverterBase = 4000,
|
InverterBase = 4000,
|
||||||
InverterSerialZero,
|
InverterSerialZero,
|
||||||
@ -31,6 +33,7 @@ enum WebApiError {
|
|||||||
InverterChanged,
|
InverterChanged,
|
||||||
InverterDeleted,
|
InverterDeleted,
|
||||||
InverterOrdered,
|
InverterOrdered,
|
||||||
|
InverterStatsResetted,
|
||||||
|
|
||||||
LimitBase = 5000,
|
LimitBase = 5000,
|
||||||
LimitSerialZero,
|
LimitSerialZero,
|
||||||
@ -59,6 +62,7 @@ enum WebApiError {
|
|||||||
MqttHassTopicLength,
|
MqttHassTopicLength,
|
||||||
MqttHassTopicCharacter,
|
MqttHassTopicCharacter,
|
||||||
MqttLwtQos,
|
MqttLwtQos,
|
||||||
|
MqttClientIdLength,
|
||||||
|
|
||||||
NetworkBase = 8000,
|
NetworkBase = 8000,
|
||||||
NetworkIpInvalid,
|
NetworkIpInvalid,
|
||||||
|
|||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiEventlogClass {
|
class WebApiEventlogClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEventlogStatus(AsyncWebServerRequest* request);
|
void onEventlogStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
18
include/WebApi_file.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
class WebApiFileClass {
|
||||||
|
public:
|
||||||
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onFileGet(AsyncWebServerRequest* request);
|
||||||
|
void onFileDelete(AsyncWebServerRequest* request);
|
||||||
|
void onFileDeleteAll(AsyncWebServerRequest* request);
|
||||||
|
void onFileListGet(AsyncWebServerRequest* request);
|
||||||
|
void onFileUploadFinish(AsyncWebServerRequest* request);
|
||||||
|
void onFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
||||||
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiFirmwareClass {
|
class WebApiFirmwareClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
|
void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
|
||||||
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiGridProfileClass {
|
class WebApiGridProfileClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onGridProfileStatus(AsyncWebServerRequest* request);
|
void onGridProfileStatus(AsyncWebServerRequest* request);
|
||||||
void onGridProfileRawdata(AsyncWebServerRequest* request);
|
void onGridProfileRawdata(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
14
include/WebApi_i18n.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
class WebApiI18nClass {
|
||||||
|
public:
|
||||||
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onI18nLanguages(AsyncWebServerRequest* request);
|
||||||
|
void onI18nLanguage(AsyncWebServerRequest* request);
|
||||||
|
};
|
||||||
@ -2,11 +2,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiInverterClass {
|
class WebApiInverterClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onInverterList(AsyncWebServerRequest* request);
|
void onInverterList(AsyncWebServerRequest* request);
|
||||||
@ -14,6 +14,5 @@ private:
|
|||||||
void onInverterEdit(AsyncWebServerRequest* request);
|
void onInverterEdit(AsyncWebServerRequest* request);
|
||||||
void onInverterDelete(AsyncWebServerRequest* request);
|
void onInverterDelete(AsyncWebServerRequest* request);
|
||||||
void onInverterOrder(AsyncWebServerRequest* request);
|
void onInverterOrder(AsyncWebServerRequest* request);
|
||||||
|
void onInverterStatReset(AsyncWebServerRequest* request);
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiLimitClass {
|
class WebApiLimitClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onLimitStatus(AsyncWebServerRequest* request);
|
void onLimitStatus(AsyncWebServerRequest* request);
|
||||||
void onLimitPost(AsyncWebServerRequest* request);
|
void onLimitPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiMaintenanceClass {
|
class WebApiMaintenanceClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onRebootPost(AsyncWebServerRequest* request);
|
void onRebootPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,19 +2,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#define MQTT_JSON_DOC_SIZE 10240
|
|
||||||
|
|
||||||
class WebApiMqttClass {
|
class WebApiMqttClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onMqttStatus(AsyncWebServerRequest* request);
|
void onMqttStatus(AsyncWebServerRequest* request);
|
||||||
void onMqttAdminGet(AsyncWebServerRequest* request);
|
void onMqttAdminGet(AsyncWebServerRequest* request);
|
||||||
void onMqttAdminPost(AsyncWebServerRequest* request);
|
void onMqttAdminPost(AsyncWebServerRequest* request);
|
||||||
String getTlsCertInfo(const char* cert);
|
String getTlsCertInfo(const char* cert);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,16 +2,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiNetworkClass {
|
class WebApiNetworkClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onNetworkStatus(AsyncWebServerRequest* request);
|
void onNetworkStatus(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,11 +2,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiNtpClass {
|
class WebApiNtpClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onNtpStatus(AsyncWebServerRequest* request);
|
void onNtpStatus(AsyncWebServerRequest* request);
|
||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onNtpAdminPost(AsyncWebServerRequest* request);
|
void onNtpAdminPost(AsyncWebServerRequest* request);
|
||||||
void onNtpTimeGet(AsyncWebServerRequest* request);
|
void onNtpTimeGet(AsyncWebServerRequest* request);
|
||||||
void onNtpTimePost(AsyncWebServerRequest* request);
|
void onNtpTimePost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiPowerClass {
|
class WebApiPowerClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPowerStatus(AsyncWebServerRequest* request);
|
void onPowerStatus(AsyncWebServerRequest* request);
|
||||||
void onPowerPost(AsyncWebServerRequest* request);
|
void onPowerPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -1,18 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
|
|
||||||
class WebApiPowerLimiterClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
|
|
||||||
class WebApiPowerMeterClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
void onTestHttpRequest(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class WebApiPrometheusClass {
|
class WebApiPrometheusClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
|
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
|
||||||
@ -17,8 +17,6 @@ private:
|
|||||||
|
|
||||||
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
|
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
|
|
||||||
enum MetricType_t {
|
enum MetricType_t {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
GAUGE,
|
GAUGE,
|
||||||
|
|||||||
@ -2,17 +2,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiSecurityClass {
|
class WebApiSecurityClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSecurityGet(AsyncWebServerRequest* request);
|
void onSecurityGet(AsyncWebServerRequest* request);
|
||||||
void onSecurityPost(AsyncWebServerRequest* request);
|
void onSecurityPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
void onAuthenticateGet(AsyncWebServerRequest* request);
|
void onAuthenticateGet(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiSysstatusClass {
|
class WebApiSysstatusClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSystemStatus(AsyncWebServerRequest* request);
|
void onSystemStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -1,18 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
|
|
||||||
class WebApiVedirectClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onVedirectStatus(AsyncWebServerRequest* request);
|
|
||||||
void onVedirectAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onVedirectAdminPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -2,12 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWebappClass {
|
class WebApiWebappClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer* _server;
|
void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len);
|
||||||
};
|
};
|
||||||
@ -1,26 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ArduinoJson.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class WebApiWsHuaweiLiveClass {
|
|
||||||
public:
|
|
||||||
WebApiWsHuaweiLiveClass();
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void generateJsonResponse(JsonVariant& root);
|
|
||||||
void onLivedataStatus(AsyncWebServerRequest* request);
|
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
|
||||||
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
|
||||||
uint32_t _lastUpdateCheck = 0;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
|
||||||
};
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ArduinoJson.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class WebApiWsBatteryLiveClass {
|
|
||||||
public:
|
|
||||||
WebApiWsBatteryLiveClass();
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void generateJsonResponse(JsonVariant& root);
|
|
||||||
void onLivedataStatus(AsyncWebServerRequest* request);
|
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
|
||||||
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
|
||||||
uint32_t _lastUpdateCheck = 0;
|
|
||||||
static constexpr uint16_t _responseSize = 1024 + 512;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
|
||||||
};
|
|
||||||
@ -2,16 +2,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWsConsoleClass {
|
class WebApiWsConsoleClass {
|
||||||
public:
|
public:
|
||||||
WebApiWsConsoleClass();
|
WebApiWsConsoleClass();
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
void reload();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
AsyncAuthenticationMiddleware _simpleDigestAuth;
|
||||||
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
Task _wsCleanupTask;
|
||||||
|
void wsCleanupTaskCb();
|
||||||
};
|
};
|
||||||
@ -1,30 +1,39 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Configuration.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWsLiveClass {
|
class WebApiWsLiveClass {
|
||||||
public:
|
public:
|
||||||
WebApiWsLiveClass();
|
WebApiWsLiveClass();
|
||||||
void init(AsyncWebServer& server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
void reload();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generateJsonResponse(JsonVariant& root);
|
static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
|
static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
|
static void generateCommonJsonResponse(JsonVariant& root);
|
||||||
|
|
||||||
|
static void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
|
||||||
|
static void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
|
||||||
|
|
||||||
void onLivedataStatus(AsyncWebServerRequest* request);
|
void onLivedataStatus(AsyncWebServerRequest* request);
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
AsyncAuthenticationMiddleware _simpleDigestAuth;
|
||||||
|
|
||||||
uint32_t _lastWsPublish = 0;
|
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
|
||||||
uint32_t _lastInvUpdateCheck = 0;
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
|
||||||
uint32_t _newestInverterTimestamp = 0;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
Task _wsCleanupTask;
|
||||||
|
void wsCleanupTaskCb();
|
||||||
|
|
||||||
|
Task _sendDataTask;
|
||||||
|
void sendDataTaskCb();
|
||||||
};
|
};
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ArduinoJson.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <VeDirectMpptController.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class WebApiWsVedirectLiveClass {
|
|
||||||
public:
|
|
||||||
WebApiWsVedirectLiveClass();
|
|
||||||
void init(AsyncWebServer& server);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void generateJsonResponse(JsonVariant& root);
|
|
||||||
void onLivedataStatus(AsyncWebServerRequest* request);
|
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
|
||||||
|
|
||||||
uint32_t _lastWsPublish = 0;
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
|
||||||
uint32_t _dataAgeMillis = 0;
|
|
||||||
static constexpr uint16_t _responseSize = 1024 + 128;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
|
||||||
};
|
|
||||||
9
include/__compiled_constants.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// The referenced values are generated by pio-scripts/auto_firmware_version.py
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *__COMPILED_GIT_HASH__;
|
||||||
|
extern const char *__COMPILED_GIT_BRANCH__;
|
||||||
|
// extern const char *__COMPILED_DATE_TIME_UTC_STR__;
|
||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#define ACCESS_POINT_NAME "OpenDTU-"
|
#define ACCESS_POINT_NAME "OpenDTU-"
|
||||||
#define ACCESS_POINT_PASSWORD "openDTU42"
|
#define ACCESS_POINT_PASSWORD "openDTU42"
|
||||||
#define ACCESS_POINT_TIMEOUT 3;
|
#define ACCESS_POINT_TIMEOUT 3
|
||||||
#define AUTH_USERNAME "admin"
|
#define AUTH_USERNAME "admin"
|
||||||
#define SECURITY_ALLOW_READONLY true
|
#define SECURITY_ALLOW_READONLY true
|
||||||
|
|
||||||
@ -22,7 +22,8 @@
|
|||||||
|
|
||||||
#define MDNS_ENABLED false
|
#define MDNS_ENABLED false
|
||||||
|
|
||||||
#define NTP_SERVER "pool.ntp.org"
|
#define NTP_SERVER_OLD "pool.ntp.org"
|
||||||
|
#define NTP_SERVER "opendtu.pool.ntp.org"
|
||||||
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
#define NTP_TIMEZONEDESCR "Europe/Berlin"
|
#define NTP_TIMEZONEDESCR "Europe/Berlin"
|
||||||
#define NTP_LONGITUDE 10.4515f
|
#define NTP_LONGITUDE 10.4515f
|
||||||
@ -98,110 +99,14 @@
|
|||||||
#define DISPLAY_SCREENSAVER true
|
#define DISPLAY_SCREENSAVER true
|
||||||
#define DISPLAY_ROTATION 2U
|
#define DISPLAY_ROTATION 2U
|
||||||
#define DISPLAY_CONTRAST 60U
|
#define DISPLAY_CONTRAST 60U
|
||||||
#define DISPLAY_LANGUAGE 0U
|
#define DISPLAY_LOCALE "en"
|
||||||
#define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL)
|
#define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL)
|
||||||
#define DISPLAY_DIAGRAM_MODE 1U
|
#define DISPLAY_DIAGRAM_MODE 1U
|
||||||
|
|
||||||
#define REACHABLE_THRESHOLD 2U
|
#define REACHABLE_THRESHOLD 2U
|
||||||
|
|
||||||
#define MAX_INVERTER_LIMIT 2250
|
|
||||||
#define VEDIRECT_ENABLED false
|
|
||||||
#define VEDIRECT_VERBOSE_LOGGING false
|
|
||||||
#define VEDIRECT_UPDATESONLY true
|
|
||||||
|
|
||||||
#define POWERMETER_ENABLED false
|
|
||||||
#define POWERMETER_INTERVAL 10
|
|
||||||
#define POWERMETER_SOURCE 2
|
|
||||||
#define POWERMETER_SDMBAUDRATE 9600
|
|
||||||
#define POWERMETER_SDMADDRESS 1
|
|
||||||
|
|
||||||
|
|
||||||
#define POWERLIMITER_ENABLED false
|
|
||||||
#define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED true
|
|
||||||
#define POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES 3
|
|
||||||
#define POWERLIMITER_BATTERY_DRAIN_STRATEGY 0
|
|
||||||
#define POWERLIMITER_INTERVAL 10
|
|
||||||
#define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true
|
|
||||||
#define POWERLIMITER_INVERTER_ID 0
|
|
||||||
#define POWERLIMITER_INVERTER_CHANNEL_ID 0
|
|
||||||
#define POWERLIMITER_TARGET_POWER_CONSUMPTION 0
|
|
||||||
#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0
|
|
||||||
#define POWERLIMITER_LOWER_POWER_LIMIT 10
|
|
||||||
#define POWERLIMITER_UPPER_POWER_LIMIT 800
|
|
||||||
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
|
|
||||||
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
|
|
||||||
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
|
|
||||||
#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0
|
|
||||||
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
|
|
||||||
#define POWERLIMITER_RESTART_HOUR -1
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC 100
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100.0
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100.0
|
|
||||||
|
|
||||||
#define BATTERY_ENABLED false
|
|
||||||
#define BATTERY_PROVIDER 0 // Pylontech CAN receiver
|
|
||||||
#define BATTERY_JKBMS_INTERFACE 0
|
|
||||||
#define BATTERY_JKBMS_POLLING_INTERVAL 5
|
|
||||||
|
|
||||||
#define HUAWEI_ENABLED false
|
|
||||||
#define HUAWEI_CAN_CONTROLLER_FREQUENCY 8000000UL
|
|
||||||
#define HUAWEI_AUTO_POWER_VOLTAGE_LIMIT 42.0
|
|
||||||
#define HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT 42.0
|
|
||||||
#define HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT 150
|
|
||||||
#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000
|
|
||||||
|
|
||||||
#define VERBOSE_LOGGING true
|
|
||||||
|
|
||||||
#define LED_BRIGHTNESS 100U
|
#define LED_BRIGHTNESS 100U
|
||||||
|
|
||||||
#define MAX_INVERTER_LIMIT 2250
|
#define MAX_INVERTER_LIMIT 2250
|
||||||
#define VEDIRECT_ENABLED false
|
|
||||||
#define VEDIRECT_VERBOSE_LOGGING false
|
|
||||||
#define VEDIRECT_UPDATESONLY true
|
|
||||||
|
|
||||||
#define POWERMETER_ENABLED false
|
#define LANG_PACK_SUFFIX ".lang.json"
|
||||||
#define POWERMETER_INTERVAL 10
|
|
||||||
#define POWERMETER_SOURCE 2
|
|
||||||
#define POWERMETER_SDMBAUDRATE 9600
|
|
||||||
#define POWERMETER_SDMADDRESS 1
|
|
||||||
|
|
||||||
|
|
||||||
#define POWERLIMITER_ENABLED false
|
|
||||||
#define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED true
|
|
||||||
#define POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES 3
|
|
||||||
#define POWERLIMITER_BATTERY_DRAIN_STRATEGY 0
|
|
||||||
#define POWERLIMITER_INTERVAL 10
|
|
||||||
#define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true
|
|
||||||
#define POWERLIMITER_INVERTER_ID 0
|
|
||||||
#define POWERLIMITER_INVERTER_CHANNEL_ID 0
|
|
||||||
#define POWERLIMITER_TARGET_POWER_CONSUMPTION 0
|
|
||||||
#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0
|
|
||||||
#define POWERLIMITER_LOWER_POWER_LIMIT 10
|
|
||||||
#define POWERLIMITER_UPPER_POWER_LIMIT 800
|
|
||||||
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
|
|
||||||
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
|
|
||||||
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
|
|
||||||
#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0
|
|
||||||
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
|
|
||||||
#define POWERLIMITER_RESTART_HOUR -1
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC 100
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100.0
|
|
||||||
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100.0
|
|
||||||
|
|
||||||
#define BATTERY_ENABLED false
|
|
||||||
#define BATTERY_PROVIDER 0 // Pylontech CAN receiver
|
|
||||||
#define BATTERY_JKBMS_INTERFACE 0
|
|
||||||
#define BATTERY_JKBMS_POLLING_INTERVAL 5
|
|
||||||
|
|
||||||
#define HUAWEI_ENABLED false
|
|
||||||
#define HUAWEI_CAN_CONTROLLER_FREQUENCY 8000000UL
|
|
||||||
#define HUAWEI_AUTO_POWER_VOLTAGE_LIMIT 42.0
|
|
||||||
#define HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT 42.0
|
|
||||||
#define HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT 150
|
|
||||||
#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000
|
|
||||||
|
|
||||||
#define VERBOSE_LOGGING true
|
|
||||||
|
|
||||||
#define LED_BRIGHTNESS 100U
|
|
||||||
|
|
||||||
#define MAX_INVERTER_LIMIT 2250
|
|
||||||
|
|||||||
9
lang/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Language Packs
|
||||||
|
|
||||||
|
This folder contains language packs for OpenDTU which can be uploaded to the
|
||||||
|
device using the "Config Management" function.
|
||||||
|
Select "Language Pack" in the restore section, select a `.json` file containing
|
||||||
|
your language and press "Restore". Afterwards all language selection drop down
|
||||||
|
menues contain the new language.
|
||||||
|
|
||||||
|
Create a pull to request to share your own language pack (or corrections) with the community.
|
||||||
696
lang/es.lang.json
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"name": "Español",
|
||||||
|
"code": "es"
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"date_format": "%d/%m/%Y %H:%M",
|
||||||
|
"offline": "Apagado",
|
||||||
|
"power_w": "%.0f W",
|
||||||
|
"power_kw": "%.1f kW",
|
||||||
|
"yield_today_wh": "Hoy: %4.0f Wh",
|
||||||
|
"yield_today_kwh": "Hoy: %.1f kWh",
|
||||||
|
"yield_total_kwh": "Total: %.1f kWh",
|
||||||
|
"yield_total_mwh": "Total: %.0f kWh"
|
||||||
|
},
|
||||||
|
"webapp": {
|
||||||
|
"menu": {
|
||||||
|
"LiveView": "Vista en directo",
|
||||||
|
"Settings": "Ajustes",
|
||||||
|
"NetworkSettings": "Ajustes de Red",
|
||||||
|
"NTPSettings": "Ajustes NTP",
|
||||||
|
"MQTTSettings": "Ajustes MQTT",
|
||||||
|
"InverterSettings": "Ajustes Inversor",
|
||||||
|
"SecuritySettings": "Ajustes Seguridad",
|
||||||
|
"DTUSettings": "Ajustes DTU",
|
||||||
|
"DeviceManager": "Administrador Dispositivos",
|
||||||
|
"ConfigManagement": "Gestión configuración",
|
||||||
|
"FirmwareUpgrade": "Actualización Firmware",
|
||||||
|
"DeviceReboot": "Reinicio Dispositivo",
|
||||||
|
"Info": "Info",
|
||||||
|
"System": "Sistema",
|
||||||
|
"Network": "Red",
|
||||||
|
"NTP": "NTP",
|
||||||
|
"MQTT": "MQTT",
|
||||||
|
"Console": "Consola",
|
||||||
|
"About": "Acerca",
|
||||||
|
"Logout": "Logout",
|
||||||
|
"Login": "Login"
|
||||||
|
},
|
||||||
|
"base": {
|
||||||
|
"Loading": "Cargando...",
|
||||||
|
"Reload": "Recargar",
|
||||||
|
"Cancel": "Cancelar",
|
||||||
|
"Save": "Guardar",
|
||||||
|
"Refreshing": "Refrescando",
|
||||||
|
"Pull": "Tira hacia abajo para refrescar",
|
||||||
|
"Release": "Soltar para refrescar",
|
||||||
|
"Close": "Cerrar",
|
||||||
|
"Yes": "Yes",
|
||||||
|
"No": "No"
|
||||||
|
},
|
||||||
|
"wait": {
|
||||||
|
"NotReady": "OpenDTU is not yet ready",
|
||||||
|
"PleaseWait": "Please wait. You will be automatically redirected to the home page."
|
||||||
|
},
|
||||||
|
"Error": {
|
||||||
|
"Oops": "Oops!"
|
||||||
|
},
|
||||||
|
"localeswitcher": {
|
||||||
|
"Dark": "Oscuro",
|
||||||
|
"Light": "Claro",
|
||||||
|
"Auto": "Automático"
|
||||||
|
},
|
||||||
|
"apiresponse": {
|
||||||
|
"1001": "¡Opciones guardadas!",
|
||||||
|
"1002": "No se encontraron valores",
|
||||||
|
"1003": "Datos demasiado grandes",
|
||||||
|
"1004": "Fallo al procesar los datos",
|
||||||
|
"1005": "Faltan valores",
|
||||||
|
"1006": "Fallo en la escritura",
|
||||||
|
"2001": "¡El número de serie no puede ser cero!",
|
||||||
|
"2002": "Intervalo de Poll interval debe ser mayor que cero!",
|
||||||
|
"2003": "Configuración de potencia incorrecta!",
|
||||||
|
"2004": "La frecuencia debe estar entre {min} y {max} kHz y debe ser un múltiplo de 250 kHz!",
|
||||||
|
"2005": "Modelo desconocido! Por favor, informe el \"Modelo de pieza de hardware\" y el modelo (por ejemplo, HM-350) como un problema en <a href=\"https://github.com/tbnobody/OpenDTU/issues\" target=\"_blank\">aquí</a>.",
|
||||||
|
"3001": "No se eliminó nada",
|
||||||
|
"3002": "Configuración borrada. Reinicio en curso...",
|
||||||
|
"4001": "@:apiresponse.2001",
|
||||||
|
"4002": "El nombre debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"4003": "Solo se admiten {max} inversores!",
|
||||||
|
"4004": "Inversor creado!",
|
||||||
|
"4005": "ID no válido especificado",
|
||||||
|
"4006": "Cantidad de canales máxima incorrecta dada!",
|
||||||
|
"4007": "Inversor modificado!",
|
||||||
|
"4008": "Inversor eliminado!",
|
||||||
|
"4009": "Orden de inversores guardado!",
|
||||||
|
"5001": "@:apiresponse.2001",
|
||||||
|
"5002": "Límite debe estar entre 1 y {max}!",
|
||||||
|
"5003": "Tipo incorrecto especificado!",
|
||||||
|
"5004": "Inversor incorrecto especificado!",
|
||||||
|
"6001": "Reinicio desencadenado!",
|
||||||
|
"6002": "Reinicio cancelado!",
|
||||||
|
"7001": "¡El servidor MQTT debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7002": "¡El nombre de usuario debe no tener más de {max} caracteres!",
|
||||||
|
"7003": "¡La contraseña debe no tener más de {max} caracteres!",
|
||||||
|
"7004": "¡El tema debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7005": "¡El tema no debe contener caracteres de espacio!",
|
||||||
|
"7006": "¡El tema debe terminar con barra inclinada (/)!",
|
||||||
|
"7007": "¡El puerto debe ser un número entre 1 y 65535!",
|
||||||
|
"7008": "¡El certificado debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7009": "¡El tema LWT debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7010": "¡El tema LWT no debe contener caracteres de espacio!",
|
||||||
|
"7011": "¡El valor LWT en línea debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7012": "¡El valor LWT fuera de línea debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7013": "¡El intervalo de publicación debe ser un número entre {min} y {max}!",
|
||||||
|
"7014": "¡El tema Hass debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"7015": "¡El tema Hass no debe contener caracteres de espacio!",
|
||||||
|
"7016": "¡La QoS LWT no debe ser mayor que {max}!",
|
||||||
|
"7017": "Client ID must not longer then {max} characters!",
|
||||||
|
"8001": "¡La dirección IP no es válida!",
|
||||||
|
"8002": "¡La máscara de red no es válida!",
|
||||||
|
"8003": "¡El gateway no es válido!",
|
||||||
|
"8004": "¡La dirección IP del servidor DNS 1 no es válida!",
|
||||||
|
"8005": "¡La dirección IP del servidor DNS 2 no es válida!",
|
||||||
|
"8006": "¡El valor de tiempo de espera del punto de acceso administrativo es inválido!",
|
||||||
|
"9001": "¡El servidor NTP debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"9002": "¡La zona horaria debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"9003": "¡La descripción de la zona horaria debe tener entre 1 y {max} caracteres de longitud!",
|
||||||
|
"9004": "¡El año debe ser un número entre {min} y {max}!",
|
||||||
|
"9005": "¡El mes debe ser un número entre {min} y {max}!",
|
||||||
|
"9006": "¡El día debe ser un número entre {min} y {max}!",
|
||||||
|
"9007": "¡La hora debe ser un número entre {min} y {max}!",
|
||||||
|
"9008": "¡Los minutos deben ser un número entre {min} y {max}!",
|
||||||
|
"9009": "¡Los segundos deben ser un número entre {min} y {max}!",
|
||||||
|
"9010": "¡Hora actualizada!",
|
||||||
|
"10001": "¡La contraseña debe tener entre 8 y {max} caracteres de longitud!",
|
||||||
|
"10002": "¡Autenticación exitosa!",
|
||||||
|
"11001": "¡@:apiresponse.2001",
|
||||||
|
"11002": "¡@:apiresponse:5004",
|
||||||
|
"12001": "¡El perfil debe tener entre 1 y {max} caracteres de longitud!"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"LiveData": "Datos en Vivo",
|
||||||
|
"SerialNumber": "Número de Serie: ",
|
||||||
|
"CurrentLimit": "Límite de Corriente: ",
|
||||||
|
"DataAge": "Edad de los Datos: ",
|
||||||
|
"Seconds": "{val} segundos",
|
||||||
|
"ShowSetInverterLimit": "Ver / Establecer Límite del Inversor",
|
||||||
|
"TurnOnOff": "Encender/Apagar el Inversor",
|
||||||
|
"ShowInverterInfo": "Ver Información del Inversor",
|
||||||
|
"ShowEventlog": "Ver Registro de Eventos",
|
||||||
|
"UnreadMessages": "mensajes sin leer",
|
||||||
|
"Loading": "@:base.Cargando",
|
||||||
|
"EventLog": "Registro de Eventos",
|
||||||
|
"InverterInfo": "Información del Inversor",
|
||||||
|
"LimitSettings": "Configuración de Límites",
|
||||||
|
"LastLimitSetStatus": "Último Estado de Configuración del Límite:",
|
||||||
|
"SetLimit": "Establecer Límite:",
|
||||||
|
"Relative": "Relativo (%)",
|
||||||
|
"Absolute": "Absoluto (W)",
|
||||||
|
"LimitHint": "<b>Consejo:</b> Si establece el límite como un valor absoluto, la visualización del valor actual solo se actualizará después de ~4 minutos.",
|
||||||
|
"SetPersistent": "Establecer Límite Permanente",
|
||||||
|
"SetNonPersistent": "Establecer Límite No Permanente",
|
||||||
|
"PowerSettings": "Configuración de Energía",
|
||||||
|
"LastPowerSetStatus": "Último Estado de Configuración de Energía:",
|
||||||
|
"TurnOn": "Encender",
|
||||||
|
"TurnOff": "Apagar",
|
||||||
|
"Restart": "Reiniciar",
|
||||||
|
"Failure": "Fallo",
|
||||||
|
"Pending": "Pendiente",
|
||||||
|
"Ok": "Aceptar",
|
||||||
|
"Unknown": "Desconocido",
|
||||||
|
"ShowGridProfile": "Ver Perfil de la Red",
|
||||||
|
"GridProfile": "Perfil de la Red",
|
||||||
|
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)",
|
||||||
|
"RadioStats": "Radio Statistics",
|
||||||
|
"TxRequest": "TX Request Count",
|
||||||
|
"RxSuccess": "RX Success",
|
||||||
|
"RxFailNothing": "RX Fail: Receive Nothing",
|
||||||
|
"RxFailPartial": "RX Fail: Receive Partial",
|
||||||
|
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
||||||
|
"TxReRequest": "TX Re-Request Fragment",
|
||||||
|
"StatsReset": "Reset Statistics",
|
||||||
|
"StatsResetting": "Resetting...",
|
||||||
|
"Rssi": "RSSI of last received packet",
|
||||||
|
"RssiHint": "HM inverters only support RSSI values < -64 dBm and > -64 dBm. In this case, -80 dbm and -30 dbm is shown.",
|
||||||
|
"dBm": "{dbm} dBm"
|
||||||
|
},
|
||||||
|
"eventlog": {
|
||||||
|
"Start": "Iniciar",
|
||||||
|
"Stop": "Parar",
|
||||||
|
"Id": "ID",
|
||||||
|
"Message": "Mensaje"
|
||||||
|
},
|
||||||
|
"devinfo": {
|
||||||
|
"NoInfo": "Sin información disponible",
|
||||||
|
"NoInfoLong": "No se ha recibido ningún dato válido del inversor hasta ahora. Todavía estamos intentando...",
|
||||||
|
"UnknownModel": "¡Modelo desconocido! Por favor, informe el \"Número de parte de hardware\" y el modelo (por ejemplo, HM-350) como un problema <a href=\"https://github.com/tbnobody/OpenDTU/issues\" target=\"_blank\">aquí</a>.",
|
||||||
|
"Serial": "Número de serie",
|
||||||
|
"ProdYear": "Año de producción",
|
||||||
|
"ProdWeek": "Semana de producción",
|
||||||
|
"Model": "Modelo",
|
||||||
|
"DetectedMaxPower": "Potencia máxima detectada",
|
||||||
|
"BootloaderVersion": "Versión del cargador de arranque",
|
||||||
|
"FirmwareVersion": "Versión del firmware",
|
||||||
|
"FirmwareBuildDate": "Fecha de construcción del firmware",
|
||||||
|
"HardwarePartNumber": "Número de parte de hardware",
|
||||||
|
"HardwareVersion": "Versión de hardware",
|
||||||
|
"SupportsPowerDistributionLogic": "'Power Distribution Logic' supported",
|
||||||
|
"Yes": "@:base.Yes",
|
||||||
|
"No": "@:base.No"
|
||||||
|
},
|
||||||
|
"gridprofile": {
|
||||||
|
"NoInfo": "@:devinfo.NoInfo",
|
||||||
|
"NoInfoLong": "@:devinfo.NoInfoLong",
|
||||||
|
"Name": "Nombre",
|
||||||
|
"Version": "Versión",
|
||||||
|
"Enabled": "@:wifistationinfo.Enabled",
|
||||||
|
"Disabled": "@:wifistationinfo.Disabled",
|
||||||
|
"GridprofileSupport": "Apoyar el desarrollo",
|
||||||
|
"GridprofileSupportLong": "Por favor, consulte <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">aquí</a> para obtener más información."
|
||||||
|
},
|
||||||
|
"systeminfo": {
|
||||||
|
"SystemInfo": "Información del sistema",
|
||||||
|
"VersionError": "Error al obtener información de la versión",
|
||||||
|
"VersionNew": "¡Nueva versión disponible! ¡Mostrar cambios!",
|
||||||
|
"VersionOk": "¡Actualizado!"
|
||||||
|
},
|
||||||
|
"firmwareinfo": {
|
||||||
|
"FirmwareInformation": "Información del firmware",
|
||||||
|
"Hostname": "Hostname",
|
||||||
|
"SdkVersion": "Versión del SDK",
|
||||||
|
"ConfigVersion": "Versión de la configuración",
|
||||||
|
"FirmwareVersion": "Versión del firmware / Hash de Git",
|
||||||
|
"PioEnv": "Entorno PIO",
|
||||||
|
"FirmwareVersionHint": "Haga clic aquí para mostrar información sobre su versión actual",
|
||||||
|
"FirmwareUpdate": "Actualización de firmware",
|
||||||
|
"FirmwareUpdateHint": "Haga clic aquí para ver las diferencias entre su versión y la última versión",
|
||||||
|
"FrmwareUpdateAllow": "Al activar la comprobación de actualización, se envía una solicitud a GitHub.com cada vez que se llama a la página para recuperar la versión actualmente disponible. Si no está de acuerdo con esto, deje esta función desactivada.",
|
||||||
|
"ResetReason0": "Razón de reinicio CPU 0",
|
||||||
|
"ResetReason1": "Razón de reinicio CPU 1",
|
||||||
|
"ConfigSaveCount": "Contador de guardado de configuración",
|
||||||
|
"Uptime": "Tiempo de actividad",
|
||||||
|
"UptimeValue": "0 días {time} | 1 día {time} | {count} días {time}"
|
||||||
|
},
|
||||||
|
"hardwareinfo": {
|
||||||
|
"HardwareInformation": "Información del hardware",
|
||||||
|
"ChipModel": "Modelo de chip",
|
||||||
|
"ChipRevision": "Revisión de chip",
|
||||||
|
"ChipCores": "Núcleos del chip",
|
||||||
|
"CpuFrequency": "Frecuencia de la CPU",
|
||||||
|
"Mhz": "MHz",
|
||||||
|
"CpuTemperature": "CPU Temperature",
|
||||||
|
"FlashSize": "Flash Memory Size"
|
||||||
|
},
|
||||||
|
"memoryinfo": {
|
||||||
|
"MemoryInformation": "Información de la memoria",
|
||||||
|
"Type": "Tipo",
|
||||||
|
"Usage": "Uso",
|
||||||
|
"Free": "Libre",
|
||||||
|
"Used": "Usado",
|
||||||
|
"Size": "Tamaño",
|
||||||
|
"Heap": "Montón",
|
||||||
|
"PsRam": "PSRAM",
|
||||||
|
"LittleFs": "LittleFs",
|
||||||
|
"Sketch": "Boceto"
|
||||||
|
},
|
||||||
|
"heapdetails": {
|
||||||
|
"HeapDetails": "Detalles del montón",
|
||||||
|
"TotalFree": "Total libre",
|
||||||
|
"LargestFreeBlock": "Bloque libre contiguo más grande",
|
||||||
|
"MaxUsage": "Uso máximo desde el inicio",
|
||||||
|
"Fragmentation": "Nivel de fragmentación"
|
||||||
|
},
|
||||||
|
"taskdetails": {
|
||||||
|
"TaskDetails": "Task Details",
|
||||||
|
"Name": "Name",
|
||||||
|
"StackFree": "Stack Free",
|
||||||
|
"Priority": "Priority",
|
||||||
|
"Task_idle0": "Idle (CPU Core 0)",
|
||||||
|
"Task_idle1": "Idle (CPU Core 1)",
|
||||||
|
"Task_wifi": "Wi-Fi",
|
||||||
|
"Task_tit": "TCP/IP",
|
||||||
|
"Task_looptask": "Arduino Main Loop",
|
||||||
|
"Task_asynctcp": "Async TCP",
|
||||||
|
"Task_mqttclient": "MQTT Client",
|
||||||
|
"Task_huaweican0": "AC Charger CAN",
|
||||||
|
"Task_pmsdm": "PowerMeter (SDM)",
|
||||||
|
"Task_pmhttpjson": "PowerMeter (HTTP+JSON)",
|
||||||
|
"Task_pmsml": "PowerMeter (Serial SML)",
|
||||||
|
"Task_pmhttpsml": "PowerMeter (HTTP+SML)"
|
||||||
|
},
|
||||||
|
"radioinfo": {
|
||||||
|
"RadioInformation": "Información de la radio",
|
||||||
|
"Status": "Estado de {module}",
|
||||||
|
"ChipStatus": "Estado del chip de {module}",
|
||||||
|
"ChipType": "Tipo de chip de {module}",
|
||||||
|
"Connected": "conectado",
|
||||||
|
"NotConnected": "no conectado",
|
||||||
|
"Configured": "configurado",
|
||||||
|
"NotConfigured": "no configurado",
|
||||||
|
"Unknown": "Desconocido"
|
||||||
|
},
|
||||||
|
"networkinfo": {
|
||||||
|
"NetworkInformation": "Información de la red"
|
||||||
|
},
|
||||||
|
"wifistationinfo": {
|
||||||
|
"WifiStationInfo": "Información de WiFi (Estación)",
|
||||||
|
"Status": "Estado",
|
||||||
|
"Enabled": "habilitado",
|
||||||
|
"Disabled": "deshabilitado",
|
||||||
|
"Ssid": "SSID",
|
||||||
|
"Bssid": "BSSID",
|
||||||
|
"Quality": "Calidad",
|
||||||
|
"Rssi": "RSSI"
|
||||||
|
},
|
||||||
|
"wifiapinfo": {
|
||||||
|
"WifiApInfo": "Información de WiFi (Punto de acceso)",
|
||||||
|
"Status": "@:wifistationinfo.Status",
|
||||||
|
"Enabled": "@:wifistationinfo.Enabled",
|
||||||
|
"Disabled": "@:wifistationinfo.Disabled",
|
||||||
|
"Ssid": "@:wifistationinfo.Ssid",
|
||||||
|
"Stations": "# Estaciones"
|
||||||
|
},
|
||||||
|
"interfacenetworkinfo": {
|
||||||
|
"NetworkInterface": "Interfaz de red ({iface})",
|
||||||
|
"Hostname": "@:firmwareinfo.Hostname",
|
||||||
|
"IpAddress": "Dirección IP",
|
||||||
|
"Netmask": "Máscara de red",
|
||||||
|
"DefaultGateway": "Puerta de enlace predeterminada",
|
||||||
|
"Dns": "DNS {num}",
|
||||||
|
"MacAddress": "Dirección MAC"
|
||||||
|
},
|
||||||
|
"interfaceapinfo": {
|
||||||
|
"NetworkInterface": "Interfaz de red (Punto de acceso)",
|
||||||
|
"IpAddress": "@:interfacenetworkinfo.IpAddress",
|
||||||
|
"MacAddress": "@:interfacenetworkinfo.MacAddress"
|
||||||
|
},
|
||||||
|
"ntpinfo": {
|
||||||
|
"NtpInformation": "Información de NTP",
|
||||||
|
"ConfigurationSummary": "Resumen de configuración",
|
||||||
|
"Server": "Servidor",
|
||||||
|
"Timezone": "Zona horaria",
|
||||||
|
"TimezoneDescription": "Descripción de la zona horaria",
|
||||||
|
"CurrentTime": "Hora actual",
|
||||||
|
"Status": "Estado",
|
||||||
|
"Synced": "sincronizado",
|
||||||
|
"NotSynced": "no sincronizado",
|
||||||
|
"LocalTime": "Hora local",
|
||||||
|
"Sunrise": "Amanecer",
|
||||||
|
"Sunset": "Atardecer",
|
||||||
|
"NotAvailable": "No disponible",
|
||||||
|
"Mode": "Modo",
|
||||||
|
"Day": "Día",
|
||||||
|
"Night": "Noche"
|
||||||
|
},
|
||||||
|
"mqttinfo": {
|
||||||
|
"MqttInformation": "Información de MQTT",
|
||||||
|
"ConfigurationSummary": "@:ntpinfo.ConfigurationSummary",
|
||||||
|
"Status": "@:ntpinfo.Status",
|
||||||
|
"Enabled": "Habilitado",
|
||||||
|
"Disabled": "Deshabilitado",
|
||||||
|
"Server": "@:ntpinfo.Server",
|
||||||
|
"Port": "Puerto",
|
||||||
|
"ClientId": "Client ID",
|
||||||
|
"Username": "Nombre de usuario",
|
||||||
|
"BaseTopic": "Tema base",
|
||||||
|
"PublishInterval": "Intervalo de publicación",
|
||||||
|
"Seconds": "{sec} segundos",
|
||||||
|
"CleanSession": "Bandera CleanSession",
|
||||||
|
"Retain": "Retener",
|
||||||
|
"Tls": "TLS",
|
||||||
|
"RootCertifcateInfo": "Información del certificado raíz de CA",
|
||||||
|
"TlsCertLogin": "Iniciar sesión con certificado TLS",
|
||||||
|
"ClientCertifcateInfo": "Información del Certificado del Cliente",
|
||||||
|
"HassSummary": "Resumen de la Configuración de Descubrimiento Automático MQTT de Home Assistant",
|
||||||
|
"Expire": "Expirar",
|
||||||
|
"IndividualPanels": "Paneles Individuales",
|
||||||
|
"RuntimeSummary": "Resumen de Tiempo de Ejecución",
|
||||||
|
"ConnectionStatus": "Estado de Conexión",
|
||||||
|
"Connected": "conectado",
|
||||||
|
"Disconnected": "desconectado"
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"Console": "Consola",
|
||||||
|
"VirtualDebugConsole": "Consola de Depuración Virtual",
|
||||||
|
"EnableAutoScroll": "Habilitar Desplazamiento Automático",
|
||||||
|
"ClearConsole": "Limpiar Consola",
|
||||||
|
"CopyToClipboard": "Copiar al Portapapeles"
|
||||||
|
},
|
||||||
|
"inverterchannelinfo": {
|
||||||
|
"String": "Cadena {num}",
|
||||||
|
"Phase": "Fase {num}",
|
||||||
|
"General": "General"
|
||||||
|
},
|
||||||
|
"invertertotalinfo": {
|
||||||
|
"TotalYieldTotal": "Total de Rendimiento Acumulado",
|
||||||
|
"TotalYieldDay": "Total de Rendimiento del Día",
|
||||||
|
"TotalPower": "Potencia Total"
|
||||||
|
},
|
||||||
|
"inverterchannelproperty": {
|
||||||
|
"Power": "Potencia",
|
||||||
|
"Voltage": "Voltaje",
|
||||||
|
"Current": "Corriente",
|
||||||
|
"Power DC": "Potencia DC",
|
||||||
|
"YieldDay": "Rendimiento del Día",
|
||||||
|
"YieldTotal": "Rendimiento Total",
|
||||||
|
"Frequency": "Frecuencia",
|
||||||
|
"Temperature": "Temperatura",
|
||||||
|
"PowerFactor": "Factor de Potencia",
|
||||||
|
"ReactivePower": "Potencia Reactiva",
|
||||||
|
"Efficiency": "Eficiencia",
|
||||||
|
"Irradiation": "Irradiación"
|
||||||
|
},
|
||||||
|
"maintenancereboot": {
|
||||||
|
"DeviceReboot": "Reinicio del Dispositivo",
|
||||||
|
"PerformReboot": "Realizar Reinicio",
|
||||||
|
"Reboot": "¡Reiniciar!",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"RebootOpenDTU": "Reiniciar OpenDTU",
|
||||||
|
"RebootQuestion": "¿Realmente desea reiniciar el dispositivo?",
|
||||||
|
"RebootHint": "<b>Nota:</b> Normalmente no es necesario realizar un reinicio manual. OpenDTU realiza cualquier reinicio necesario (por ejemplo, después de una actualización de firmware) automáticamente. También se adoptan configuraciones sin reiniciar. Si necesita reiniciar debido a un error, considere informarlo en <a href=\"https://github.com/tbnobody/OpenDTU/issues\" class=\"alert-link\" target=\"_blank\">https://github.com/tbnobody/OpenDTU/issues</a>."
|
||||||
|
},
|
||||||
|
"dtuadmin": {
|
||||||
|
"DtuSettings": "Configuración de DTU",
|
||||||
|
"DtuConfiguration": "Configuración de DTU",
|
||||||
|
"Serial": "Serial",
|
||||||
|
"SerialHint": "Tanto el inversor como el DTU tienen un número de serie. El número de serie del DTU se genera aleatoriamente en el primer inicio y generalmente no es necesario cambiarlo.",
|
||||||
|
"PollInterval": "Intervalo de Sondeo",
|
||||||
|
"Seconds": "Segundos",
|
||||||
|
"NrfPaLevel": "Potencia de Transmisión NRF24",
|
||||||
|
"CmtPaLevel": "Potencia de Transmisión CMT2300A",
|
||||||
|
"NrfPaLevelHint": "Utilizado para inversores HM. Asegúrese de que su fuente de alimentación sea lo suficientemente estable antes de aumentar la potencia de transmisión.",
|
||||||
|
"CmtPaLevelHint": "Utilizado para inversores HMS/HMT. Asegúrese de que su fuente de alimentación sea lo suficientemente estable antes de aumentar la potencia de transmisión.",
|
||||||
|
"CmtCountry": "Región/País CMT2300A",
|
||||||
|
"CmtCountryHint": "Cada país tiene asignaciones de frecuencia diferentes.",
|
||||||
|
"country_0": "Europa ({min}MHz - {max}MHz)",
|
||||||
|
"country_1": "América del Norte ({min}MHz - {max}MHz)",
|
||||||
|
"country_2": "Brasil ({min}MHz - {max}MHz)",
|
||||||
|
"CmtFrequency": "Frecuencia CMT2300A",
|
||||||
|
"CmtFrequencyHint": "¡Asegúrese de utilizar solo frecuencias permitidas en el país respectivo! Después de un cambio de frecuencia, puede tardar hasta 15 minutos en establecer una conexión.",
|
||||||
|
"CmtFrequencyWarning": "La frecuencia seleccionada está fuera del rango permitido en su región/país seleccionado. Asegúrese de que esta selección no infrinja ninguna regulación local.",
|
||||||
|
"MHz": "{mhz} MHz",
|
||||||
|
"dBm": "{dbm} dBm",
|
||||||
|
"Min": "Mínimo ({db} dBm)",
|
||||||
|
"Low": "Bajo ({db} dBm)",
|
||||||
|
"High": "Alto ({db} dBm)",
|
||||||
|
"Max": "Máximo ({db} dBm)"
|
||||||
|
},
|
||||||
|
"securityadmin": {
|
||||||
|
"SecuritySettings": "Configuración de Seguridad",
|
||||||
|
"AdminPassword": "Contraseña de Administrador",
|
||||||
|
"Password": "Contraseña",
|
||||||
|
"RepeatPassword": "Repetir Contraseña",
|
||||||
|
"PasswordHint": "<b>Consejo:</b> La contraseña de administrador se utiliza para acceder a esta interfaz web (usuario 'admin'), pero también para conectarse al dispositivo cuando está en modo AP. Debe tener 8 a 64 caracteres.",
|
||||||
|
"Permissions": "Permisos",
|
||||||
|
"ReadOnly": "Permitir acceso de solo lectura a la interfaz web sin contraseña"
|
||||||
|
},
|
||||||
|
"ntpadmin": {
|
||||||
|
"NtpSettings": "Configuración de NTP",
|
||||||
|
"NtpConfiguration": "Configuración de NTP",
|
||||||
|
"TimeServer": "Servidor de Tiempo",
|
||||||
|
"TimeServerHint": "El valor predeterminado es adecuado siempre que OpenDTU tenga acceso directo a Internet.",
|
||||||
|
"Timezone": "Zona Horaria",
|
||||||
|
"TimezoneConfig": "Configuración de Zona Horaria",
|
||||||
|
"LocationConfiguration": "Configuración de Ubicación",
|
||||||
|
"Longitude": "Longitud",
|
||||||
|
"Latitude": "Latitud",
|
||||||
|
"SunSetType": "Tipo de Atardecer",
|
||||||
|
"SunSetTypeHint": "Afecta al cálculo día/noche. Puede tardar hasta un minuto en aplicarse el nuevo tipo.",
|
||||||
|
"OFFICIAL": "Amanecer estándar (90.8°)",
|
||||||
|
"NAUTICAL": "Amanecer náutico (102°)",
|
||||||
|
"CIVIL": "Amanecer civil (96°)",
|
||||||
|
"ASTONOMICAL": "Amanecer astronómico (108°)",
|
||||||
|
"ManualTimeSynchronization": "Sincronización Manual del Tiempo",
|
||||||
|
"CurrentOpenDtuTime": "Hora Actual de OpenDTU",
|
||||||
|
"CurrentLocalTime": "Hora Local Actual",
|
||||||
|
"SynchronizeTime": "Sincronizar Tiempo",
|
||||||
|
"SynchronizeTimeHint": "<b>Consejo:</b> Puede utilizar la sincronización manual del tiempo para establecer la hora actual de OpenDTU si no hay un servidor NTP disponible. Pero tenga en cuenta que en caso de un ciclo de energía, se perderá la hora. Además, tenga en cuenta que la precisión del tiempo se verá gravemente afectada, ya que no se puede resincronizar regularmente y el microcontrolador ESP32 no tiene un reloj en tiempo real."
|
||||||
|
},
|
||||||
|
"networkadmin": {
|
||||||
|
"NetworkSettings": "Configuración de Red",
|
||||||
|
"WifiConfiguration": "Configuración de WiFi",
|
||||||
|
"WifiSsid": "SSID de WiFi",
|
||||||
|
"WifiPassword": "Contraseña de WiFi",
|
||||||
|
"Hostname": "Nombre de Host",
|
||||||
|
"HostnameHint": "<b>Consejo:</b> El texto <span class=\"font-monospace\">%06X</span> se remplazará con los últimos 6 dígitos del ChipID de ESP en formato hexadecimal.",
|
||||||
|
"EnableDhcp": "Habilitar DHCP",
|
||||||
|
"StaticIpConfiguration": "Configuración de IP Estática",
|
||||||
|
"IpAddress": "Dirección IP",
|
||||||
|
"Netmask": "Máscara de Red",
|
||||||
|
"DefaultGateway": "Puerta de Enlace Predeterminada",
|
||||||
|
"Dns": "Servidor DNS {num}",
|
||||||
|
"AdminAp": "Configuración de WiFi (Punto de Acceso de Administrador)",
|
||||||
|
"ApTimeout": "Tiempo de espera del Punto de Acceso",
|
||||||
|
"ApTimeoutHint": "Tiempo que se mantiene abierto el Punto de Acceso. Un valor de 0 significa infinito.",
|
||||||
|
"Minutes": "minutos",
|
||||||
|
"EnableMdns": "Habilitar mDNS",
|
||||||
|
"MdnsSettings": "Configuración de mDNS"
|
||||||
|
},
|
||||||
|
"mqttadmin": {
|
||||||
|
"MqttSettings": "Configuración de MQTT",
|
||||||
|
"MqttConfiguration": "Configuración de MQTT",
|
||||||
|
"EnableMqtt": "Habilitar MQTT",
|
||||||
|
"EnableHass": "Habilitar Descubrimiento Automático MQTT de Home Assistant",
|
||||||
|
"MqttBrokerParameter": "Parámetros del Broker MQTT",
|
||||||
|
"Hostname": "Nombre de Host",
|
||||||
|
"HostnameHint": "Nombre de host o dirección IP",
|
||||||
|
"Port": "Puerto",
|
||||||
|
"ClientId": "Client ID",
|
||||||
|
"Username": "Nombre de Usuario",
|
||||||
|
"UsernameHint": "Nombre de usuario, dejar vacío para conexión anónima",
|
||||||
|
"Password": "Contraseña",
|
||||||
|
"PasswordHint": "Contraseña, dejar vacío para conexión anónima",
|
||||||
|
"BaseTopic": "Tema Base",
|
||||||
|
"BaseTopicHint": "Tema base, se antepondrá a todos los temas publicados (por ejemplo, inverter/)",
|
||||||
|
"PublishInterval": "Intervalo de Publicación",
|
||||||
|
"Seconds": "segundos",
|
||||||
|
"CleanSession": "Habilitar Bandera CleanSession",
|
||||||
|
"EnableRetain": "Habilitar Bandera Retain",
|
||||||
|
"EnableTls": "Habilitar TLS",
|
||||||
|
"RootCa": "Certificado Raíz CA (predeterminado Letsencrypt)",
|
||||||
|
"TlsCertLoginEnable": "Habilitar Inicio de Sesión con Certificado TLS",
|
||||||
|
"ClientCert": "Certificado del Cliente TLS",
|
||||||
|
"ClientKey": "Clave del Cliente TLS",
|
||||||
|
"LwtParameters": "Parámetros de LWT",
|
||||||
|
"LwtTopic": "Tema de LWT",
|
||||||
|
"LwtTopicHint": "Tema de LWT, se añadirá al tema base",
|
||||||
|
"LwtOnline": "Mensaje de LWT en línea",
|
||||||
|
"LwtOnlineHint": "Mensaje que se publicará en el tema de LWT cuando esté en línea",
|
||||||
|
"LwtOffline": "Mensaje de LWT fuera de línea",
|
||||||
|
"LwtOfflineHint": "Mensaje que se publicará en el tema de LWT cuando esté fuera de línea",
|
||||||
|
"LwtQos": "QoS (Calidad de Servicio)",
|
||||||
|
"QOS0": "0 (Como máximo una vez)",
|
||||||
|
"QOS1": "1 (Al menos una vez)",
|
||||||
|
"QOS2": "2 (Exactamente una vez)",
|
||||||
|
"HassParameters": "Parámetros de Descubrimiento Automático MQTT de Home Assistant",
|
||||||
|
"HassPrefixTopic": "Tema de Prefijo",
|
||||||
|
"HassPrefixTopicHint": "El prefijo para el tema de descubrimiento",
|
||||||
|
"HassRetain": "Habilitar Bandera Retain",
|
||||||
|
"HassExpire": "Habilitar Expiración",
|
||||||
|
"HassIndividual": "Paneles Individuales"
|
||||||
|
},
|
||||||
|
"inverteradmin": {
|
||||||
|
"InverterSettings": "Configuración del Inversor",
|
||||||
|
"AddInverter": "Agregar un nuevo Inversor",
|
||||||
|
"Serial": "Serial",
|
||||||
|
"Name": "Nombre",
|
||||||
|
"Add": "Agregar",
|
||||||
|
"AddHint": "<b>Consejo:</b> Puede configurar parámetros adicionales después de haber creado el inversor. Use el ícono de lápiz en la lista de inversores.",
|
||||||
|
"InverterList": "Lista de Inversores",
|
||||||
|
"Status": "Estado",
|
||||||
|
"Send": "Enviar",
|
||||||
|
"Receive": "Recibir",
|
||||||
|
"StatusHint": "<b>Consejo:</b> El inversor se alimenta con su entrada de CC. Si no hay sol, el inversor está apagado. Aún se pueden enviar solicitudes.",
|
||||||
|
"Type": "Tipo",
|
||||||
|
"Action": "Acción",
|
||||||
|
"SaveOrder": "Guardar orden",
|
||||||
|
"DeleteInverter": "Eliminar inversor",
|
||||||
|
"EditInverter": "Editar inversor",
|
||||||
|
"General": "General",
|
||||||
|
"String": "Cadena",
|
||||||
|
"Advanced": "Avanzado",
|
||||||
|
"InverterSerial": "Serial del Inversor:",
|
||||||
|
"InverterName": "Nombre del Inversor:",
|
||||||
|
"InverterNameHint": "Aquí puede especificar un nombre personalizado para su inversor.",
|
||||||
|
"InverterStatus": "Recibir / Enviar",
|
||||||
|
"PollEnable": "Sondear datos del inversor",
|
||||||
|
"PollEnableNight": "Sondear datos del inversor por la noche",
|
||||||
|
"CommandEnable": "Enviar comandos",
|
||||||
|
"CommandEnableNight": "Enviar comandos por la noche",
|
||||||
|
"StringName": "Nombre de cadena {num}:",
|
||||||
|
"StringNameHint": "Aquí puede especificar un nombre personalizado para el puerto respectivo de su inversor.",
|
||||||
|
"StringMaxPower": "Potencia máxima de cadena {num}:",
|
||||||
|
"StringMaxPowerHint": "Ingrese la potencia máxima de los paneles solares conectados.",
|
||||||
|
"StringYtOffset": "Compensación total de rendimiento de cadena {num}:",
|
||||||
|
"StringYtOffsetHint": "Esta compensación se aplica al valor total de rendimiento leído del inversor. Esto se puede usar para ajustar el rendimiento total del inversor a cero si se utiliza un inversor usado. Pero aún puede intentar sondear datos.",
|
||||||
|
"InverterHint": "*) Ingrese W<sub>p</sub> del canal para calcular la irradiación.",
|
||||||
|
"ReachableThreshold": "Umbral de Alcanzabilidad",
|
||||||
|
"ReachableThresholdHint": "Define cuántas solicitudes se permiten fallar hasta que el inversor se considere no alcanzable.",
|
||||||
|
"ZeroRuntime": "Datos de tiempo cero",
|
||||||
|
"ZeroRuntimeHint": "Datos de tiempo cero (sin datos de rendimiento) si el inversor se vuelve inalcanzable.",
|
||||||
|
"ZeroDay": "Rendimiento diario cero a medianoche",
|
||||||
|
"ZeroDayHint": "Esto solo funciona si el inversor es inalcanzable. Si se leen datos del inversor, se usarán sus valores. (El reinicio solo ocurre en el ciclo de energía)",
|
||||||
|
"ClearEventlog": "Clear Eventlog at midnight",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"Save": "@:base.Save",
|
||||||
|
"DeleteMsg": "¿Está seguro de que desea eliminar el inversor \"{name}\" con número de serie {serial}?",
|
||||||
|
"Delete": "Eliminar",
|
||||||
|
"YieldDayCorrection": "Corrección de Rendimiento Diario",
|
||||||
|
"YieldDayCorrectionHint": "Sumar el rendimiento diario incluso si el inversor se reinicia. El valor se restablecerá a medianoche"
|
||||||
|
},
|
||||||
|
"fileadmin": {
|
||||||
|
"ConfigManagement": "Gestión de Configuración",
|
||||||
|
"BackupHeader": "Copia de seguridad: Copia de Seguridad del Archivo de Configuración",
|
||||||
|
"BackupConfig": "Copia de seguridad del archivo de configuración",
|
||||||
|
"Backup": "Copia de seguridad",
|
||||||
|
"Restore": "Restaurar",
|
||||||
|
"NoFileSelected": "Ningún archivo seleccionado",
|
||||||
|
"RestoreHeader": "Restaurar: Restaurar el Archivo de Configuración",
|
||||||
|
"Back": "Atrás",
|
||||||
|
"UploadSuccess": "Carga Exitosa",
|
||||||
|
"RestoreHint": "<b>Nota:</b> Esta operación reemplaza el archivo de configuración con la configuración restaurada y reinicia OpenDTU para aplicar todas las configuraciones.",
|
||||||
|
"ResetHeader": "Inicializar: Realizar Restablecimiento de Fábrica",
|
||||||
|
"FactoryResetButton": "Restaurar Configuraciones Predeterminadas de Fábrica",
|
||||||
|
"ResetHint": "<b>Nota:</b> Haga clic en Restaurar Configuraciones Predeterminadas de Fábrica para restaurar e inicializar las configuraciones predeterminadas de fábrica y reiniciar.",
|
||||||
|
"FactoryReset": "Restablecimiento de Fábrica",
|
||||||
|
"ResetMsg": "¿Está seguro de que desea eliminar la configuración actual y restablecer todas las configuraciones a sus valores predeterminados de fábrica?",
|
||||||
|
"ResetConfirm": "Restablecimiento de Fábrica",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"InvalidJson": "JSON file is formatted incorrectly.",
|
||||||
|
"InvalidJsonContent": "JSON file has the wrong content."
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"Login": "Iniciar Sesión",
|
||||||
|
"SystemLogin": "Inicio de Sesión en el Sistema",
|
||||||
|
"Username": "Nombre de Usuario",
|
||||||
|
"UsernameRequired": "Se requiere el nombre de usuario",
|
||||||
|
"Password": "Contraseña",
|
||||||
|
"PasswordRequired": "Se requiere la contraseña",
|
||||||
|
"LoginButton": "Iniciar Sesión"
|
||||||
|
},
|
||||||
|
"firmwareupgrade": {
|
||||||
|
"FirmwareUpgrade": "Actualización de Firmware",
|
||||||
|
"Loading": "@:base.Loading",
|
||||||
|
"OtaError": "Error OTA",
|
||||||
|
"Back": "Atrás",
|
||||||
|
"Retry": "Reintentar",
|
||||||
|
"OtaStatus": "Estado OTA",
|
||||||
|
"OtaSuccess": "La carga de firmware fue exitosa. El dispositivo se reinició automáticamente. Cuando el dispositivo vuelva a ser accesible, la interfaz se recargará automáticamente.",
|
||||||
|
"FirmwareUpload": "Carga de Firmware",
|
||||||
|
"UploadProgress": "Progreso de Carga"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"AboutOpendtu": "Acerca de OpenDTU",
|
||||||
|
"Documentation": "Documentation",
|
||||||
|
"DocumentationBody": "The firmware and hardware documentation can be found here: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
|
||||||
|
"ProjectOrigin": "Origen del Proyecto",
|
||||||
|
"ProjectOriginBody1": "Este proyecto se inició a partir de <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">esta discusión. (Mikrocontroller.net)</a>",
|
||||||
|
"ProjectOriginBody2": "El protocolo de Hoymiles fue descifrado mediante los esfuerzos voluntarios de muchos participantes. OpenDTU, entre otros, se desarrolló basado en este trabajo. El proyecto está bajo una Licencia de Código Abierto (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">Licencia Pública General de GNU versión 2</a>).",
|
||||||
|
"ProjectOriginBody3": "El software se desarrolló según nuestro mejor conocimiento y creencia. Sin embargo, no se acepta ninguna responsabilidad por un mal funcionamiento o pérdida de garantía del inversor.",
|
||||||
|
"ProjectOriginBody4": "OpenDTU está disponible de forma gratuita. Si pagaste dinero por el software, probablemente te estafaron.",
|
||||||
|
"NewsUpdates": "Noticias y Actualizaciones",
|
||||||
|
"NewsUpdatesBody": "Las nuevas actualizaciones se pueden encontrar en Github: <a href=\"https://github.com/tbnobody/OpenDTU\" target=\"_blank\">https://github.com/tbnobody/OpenDTU</a>",
|
||||||
|
"ErrorReporting": "Reporte de Errores",
|
||||||
|
"ErrorReportingBody": "Por favor, informa problemas utilizando la función proporcionada por <a href=\"https://github.com/tbnobody/OpenDTU/issues\" target=\"_blank\">Github</a>",
|
||||||
|
"Discussion": "Discusión",
|
||||||
|
"DiscussionBody": "Discute con nosotros en <a href=\"https://discord.gg/WzhxEY62mB\" target=\"_blank\">Discord</a> o <a href=\"https://github.com/tbnobody/OpenDTU/discussions\" target=\"_blank\">Github</a>"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"RadioProblem": "No se pudo conectar a un módulo de radio configurado. Por favor, verifica la conexión.",
|
||||||
|
"TimeSync": "El reloj aún no ha sido sincronizado. Sin un reloj correctamente ajustado, no se realizan solicitudes al inversor. Esto es normal poco después del inicio. Sin embargo, después de un tiempo de ejecución más largo (>1 minuto), indica que el servidor NTP no es accesible.",
|
||||||
|
"TimeSyncLink": "Por favor, verifica la configuración de tu hora.",
|
||||||
|
"DefaultPassword": "Estás utilizando la contraseña predeterminada para la interfaz web y el punto de acceso de emergencia. Esto potencialmente es inseguro.",
|
||||||
|
"DefaultPasswordLink": "Por favor, cambia la contraseña.",
|
||||||
|
"PinMappingIssue": "You are using a generic firmware image, but have not yet uploaded a file with device profiles (<code>pin_mapping.json</code>) or have not selected a profile defined there. Please refer to the <a href=\"https://opendtu.solar/firmware/device_profiles/\" target=\"_blank\" class=\"alert-link\">documentation</a> for details."
|
||||||
|
},
|
||||||
|
"deviceadmin": {
|
||||||
|
"DeviceManager": "Administrador de Dispositivos",
|
||||||
|
"ParseError": "Error de análisis en 'pin_mapping.json': {error}",
|
||||||
|
"PinAssignment": "Configuración de Conexión",
|
||||||
|
"SelectedProfile": "Perfil Seleccionado",
|
||||||
|
"DefaultProfile": "(Configuraciones predeterminadas)",
|
||||||
|
"ProfileHint": "Tu dispositivo puede dejar de responder si seleccionas un perfil incompatible. En este caso, debes realizar una eliminación a través de la interfaz serial.",
|
||||||
|
"Display": "Pantalla",
|
||||||
|
"PowerSafe": "Habilitar Ahorro de Energía",
|
||||||
|
"PowerSafeHint": "Apaga la pantalla si no hay un inversor produciendo.",
|
||||||
|
"Screensaver": "Habilitar Protector de Pantalla",
|
||||||
|
"ScreensaverHint": "Mueve la pantalla un poco en cada actualización para evitar el quemado. (Útil especialmente para pantallas OLED)",
|
||||||
|
"DiagramMode": "Modo de Diagrama",
|
||||||
|
"off": "Apagar",
|
||||||
|
"small": "Pequeño",
|
||||||
|
"fullscreen": "Pantalla Completa",
|
||||||
|
"DiagramDuration": "Duración del Diagrama",
|
||||||
|
"DiagramDurationHint": "El período de tiempo que se muestra en el diagrama.",
|
||||||
|
"Seconds": "Segundos",
|
||||||
|
"Contrast": "Contraste ({contrast})",
|
||||||
|
"Rotation": "Rotación",
|
||||||
|
"rot0": "Sin rotación",
|
||||||
|
"rot90": "Rotación de 90 grados",
|
||||||
|
"rot180": "Rotación de 180 grados",
|
||||||
|
"rot270": "Rotación de 270 grados",
|
||||||
|
"DisplayLanguage": "Idioma de la Pantalla",
|
||||||
|
"en": "Inglés",
|
||||||
|
"de": "Alemán",
|
||||||
|
"fr": "Francés",
|
||||||
|
"Leds": "LEDs",
|
||||||
|
"EqualBrightness": "Brillo Equitativo",
|
||||||
|
"LedBrightness": "Brillo del LED {led} ({brightness})"
|
||||||
|
},
|
||||||
|
"pininfo": {
|
||||||
|
"Category": "Categoría",
|
||||||
|
"Name": "Nombre",
|
||||||
|
"Number": "Número",
|
||||||
|
"ValueSelected": "Seleccionado",
|
||||||
|
"ValueActive": "Activo"
|
||||||
|
},
|
||||||
|
"inputserial": {
|
||||||
|
"format_hoymiles": "Hoymiles serial number format",
|
||||||
|
"format_converted": "Already converted serial number",
|
||||||
|
"format_herf_valid": "E-Star HERF format (will be saved converted): {serial}",
|
||||||
|
"format_herf_invalid": "E-Star HERF format: Invalid checksum",
|
||||||
|
"format_unknown": "Unknown format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||