Compare commits
486 Commits
2024.01.17
...
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 |
39
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
39
.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:
|
||||||
@ -65,4 +81,17 @@ body:
|
|||||||
Links? References? Anything that will give us more context about the issue you are encountering!
|
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||||
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
|
||||||
|
|||||||
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@ -18,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"
|
||||||
|
|
||||||
@ -43,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:
|
||||||
@ -56,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') }}
|
||||||
@ -64,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"
|
||||||
|
|
||||||
@ -79,18 +79,27 @@ 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 }}
|
||||||
@ -119,7 +128,7 @@ jobs:
|
|||||||
|
|
||||||
- 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
|
||||||
@ -138,7 +147,7 @@ jobs:
|
|||||||
for i in */; do cp ${i}opendtu-*.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": [
|
||||||
|
|||||||
4
.github/workflows/cpplint.yml
vendored
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
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.
|
||||||
16
.github/workflows/yarnlint.yml
vendored
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
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
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": [
|
||||||
|
|||||||
71
README.md
71
README.md
@ -23,6 +23,10 @@ Please feel free to support and create a PR in [this](https://github.com/tbnobod
|
|||||||
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
||||||
|
|
||||||
```code
|
```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
|
* 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
|
* 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=
|
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
|
||||||
@ -36,69 +40,4 @@ Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | gre
|
|||||||
|
|
||||||
## Currently supported Inverters
|
## Currently supported Inverters
|
||||||
|
|
||||||
| Model | Required RF Module | DC Inputs | MPP-Tracker | AC Phases |
|
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||||
| ---------------------| ------------------ | --------- | ----------- | --------- |
|
|
||||||
| Hoymiles HM-300-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-350-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-400-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-600-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-700-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-800-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1000-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1200-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1500-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-300-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-350-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-400-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-450-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-500-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-600-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-700-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-800-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-900-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1000-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1600-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-1800-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-2000-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMT-1600-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-1800-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-2000-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-1800-6T | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Hoymiles HMT-2250-6T | 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 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
### 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](docs/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
|
|
||||||
```
|
|
||||||
|
|
||||||
It is recommended to make all changes only in the 'platformio_override.ini', this is your personal copy.
|
|
||||||
|
|||||||
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
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
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
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": [
|
||||||
@ -78,4 +106,4 @@
|
|||||||
"clk": 17
|
"clk": 17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -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,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.
|
|
||||||
|
|||||||
@ -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,6 +19,7 @@
|
|||||||
#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 32
|
#define MQTT_MAX_TOPIC_STRLEN 32
|
||||||
@ -29,8 +33,7 @@
|
|||||||
#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 JSON_BUFFER_SIZE 12288
|
|
||||||
|
|
||||||
struct CHANNEL_CONFIG_T {
|
struct CHANNEL_CONFIG_T {
|
||||||
uint16_t MaxChannelPower;
|
uint16_t MaxChannelPower;
|
||||||
@ -49,6 +52,7 @@ 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];
|
||||||
};
|
};
|
||||||
@ -89,6 +93,7 @@ struct CONFIG_T {
|
|||||||
bool Enabled;
|
bool Enabled;
|
||||||
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
||||||
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];
|
||||||
@ -143,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;
|
||||||
@ -160,14 +165,32 @@ struct CONFIG_T {
|
|||||||
|
|
||||||
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
|
||||||
@ -81,4 +82,4 @@ private:
|
|||||||
bool _isAtLeastOnePollEnabled = false;
|
bool _isAtLeastOnePollEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DatastoreClass Datastore;
|
extern DatastoreClass Datastore;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
35
include/I18n.h
Normal file
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,33 +1,34 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AsyncWebSocket.h>
|
#include <AsyncWebSocket.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#define BUFFER_SIZE 500
|
#define BUFFER_SIZE 500
|
||||||
|
|
||||||
class MessageOutputClass : public Print {
|
class MessageOutputClass : public Print {
|
||||||
public:
|
public:
|
||||||
void init(Scheduler& scheduler);
|
MessageOutputClass();
|
||||||
size_t write(uint8_t c) override;
|
void init(Scheduler& scheduler);
|
||||||
size_t write(const uint8_t* buffer, size_t size) override;
|
size_t write(uint8_t c) override;
|
||||||
void register_ws_output(AsyncWebSocket* output);
|
size_t write(const uint8_t* buffer, size_t size) override;
|
||||||
|
void register_ws_output(AsyncWebSocket* output);
|
||||||
private:
|
|
||||||
void loop();
|
private:
|
||||||
|
void loop();
|
||||||
Task _loopTask;
|
|
||||||
|
Task _loopTask;
|
||||||
AsyncWebSocket* _ws = nullptr;
|
|
||||||
char _buffer[BUFFER_SIZE];
|
AsyncWebSocket* _ws = nullptr;
|
||||||
uint16_t _buff_pos = 0;
|
char _buffer[BUFFER_SIZE];
|
||||||
uint32_t _lastSend = 0;
|
uint16_t _buff_pos = 0;
|
||||||
bool _forceSend = false;
|
uint32_t _lastSend = 0;
|
||||||
|
bool _forceSend = false;
|
||||||
std::mutex _msgLock;
|
|
||||||
};
|
std::mutex _msgLock;
|
||||||
|
};
|
||||||
extern MessageOutputClass MessageOutput;
|
|
||||||
|
extern MessageOutputClass MessageOutput;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
class MqttHandleDtuClass {
|
class MqttHandleDtuClass {
|
||||||
public:
|
public:
|
||||||
|
MqttHandleDtuClass();
|
||||||
void init(Scheduler& scheduler);
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -14,4 +15,4 @@ private:
|
|||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleDtuClass MqttHandleDtu;
|
extern MqttHandleDtuClass MqttHandleDtu;
|
||||||
|
|||||||
@ -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();
|
||||||
@ -79,4 +104,4 @@ private:
|
|||||||
bool _updateForced = false;
|
bool _updateForced = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleHassClass MqttHandleHass;
|
extern MqttHandleHassClass MqttHandleHass;
|
||||||
|
|||||||
@ -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:
|
||||||
@ -13,4 +14,4 @@ private:
|
|||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterTotalClass MqttHandleInverterTotal;
|
extern MqttHandleInverterTotalClass MqttHandleInverterTotal;
|
||||||
|
|||||||
@ -20,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);
|
||||||
@ -39,4 +40,4 @@ private:
|
|||||||
std::mutex _clientLock;
|
std::mutex _clientLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
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,11 +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 led[PINMAPPING_LED_COUNT];
|
int8_t led[PINMAPPING_LED_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,12 +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;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PinMapping_t _pinMapping;
|
PinMapping_t _pinMapping;
|
||||||
|
|
||||||
|
bool _mappingSelected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PinMappingClass PinMapping;
|
extern PinMappingClass PinMapping;
|
||||||
|
|||||||
18
include/RestartHelper.h
Normal file
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);
|
||||||
};
|
};
|
||||||
|
|||||||
29
include/W5500.h
Normal file
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,14 +1,15 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#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"
|
||||||
@ -22,6 +23,7 @@
|
|||||||
#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 <AsyncJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
@ -29,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);
|
||||||
@ -37,20 +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;
|
||||||
|
|
||||||
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;
|
||||||
@ -66,4 +70,4 @@ private:
|
|||||||
WebApiWsLiveClass _webApiWsLive;
|
WebApiWsLiveClass _webApiWsLive;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WebApiClass WebApi;
|
extern WebApiClass WebApi;
|
||||||
|
|||||||
@ -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
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
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;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -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,
|
||||||
@ -47,4 +45,4 @@ private:
|
|||||||
{ FLD_EFF, MetricType_t::GAUGE },
|
{ FLD_EFF, MetricType_t::GAUGE },
|
||||||
{ FLD_IRR, MetricType_t::GAUGE },
|
{ FLD_IRR, MetricType_t::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;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -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,17 +1,19 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
class WebApiWsConsoleClass {
|
|
||||||
public:
|
class WebApiWsConsoleClass {
|
||||||
WebApiWsConsoleClass();
|
public:
|
||||||
void init(AsyncWebServer& server);
|
WebApiWsConsoleClass();
|
||||||
void loop();
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
void reload();
|
||||||
private:
|
|
||||||
AsyncWebServer* _server;
|
private:
|
||||||
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();
|
||||||
|
};
|
||||||
|
|||||||
9
include/__compiled_constants.h
Normal file
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,7 +99,7 @@
|
|||||||
#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
|
||||||
|
|
||||||
@ -107,3 +108,5 @@
|
|||||||
#define LED_BRIGHTNESS 100U
|
#define LED_BRIGHTNESS 100U
|
||||||
|
|
||||||
#define MAX_INVERTER_LIMIT 2250
|
#define MAX_INVERTER_LIMIT 2250
|
||||||
|
|
||||||
|
#define LANG_PACK_SUFFIX ".lang.json"
|
||||||
|
|||||||
9
lang/README.md
Normal file
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
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
696
lang/it.lang.json
Normal file
696
lang/it.lang.json
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"name": "Italiano",
|
||||||
|
"code": "it"
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"date_format": "%d/%m/%Y %H:%M",
|
||||||
|
"offline": "Offline",
|
||||||
|
"power_w": "%.0f W",
|
||||||
|
"power_kw": "%.1f kW",
|
||||||
|
"yield_today_wh": "oggi: %4.0f Wh",
|
||||||
|
"yield_today_kwh": "oggi: %.1f kWh",
|
||||||
|
"yield_total_kwh": "totale: %.1f kWh",
|
||||||
|
"yield_total_mwh": "totale: %.0f kWh"
|
||||||
|
},
|
||||||
|
"webapp": {
|
||||||
|
"menu": {
|
||||||
|
"LiveView": "Dati in tempo reale",
|
||||||
|
"Settings": "Impostazioni",
|
||||||
|
"NetworkSettings": "Impostazioni di rete",
|
||||||
|
"NTPSettings": "Impostazioni NTP",
|
||||||
|
"MQTTSettings": "Impostazioni MQTT",
|
||||||
|
"InverterSettings": "Impostazioni Inverter",
|
||||||
|
"SecuritySettings": "Impostazioni di Sicurezza",
|
||||||
|
"DTUSettings": "Impostazioni DTU",
|
||||||
|
"DeviceManager": "Gestione Dispositivi",
|
||||||
|
"ConfigManagement": "Gestione Configurazione",
|
||||||
|
"FirmwareUpgrade": "Aggiornamento Firmware",
|
||||||
|
"DeviceReboot": "Riavvio DTU",
|
||||||
|
"Info": "Info",
|
||||||
|
"System": "Sistema",
|
||||||
|
"Network": "Rete",
|
||||||
|
"NTP": "NTP",
|
||||||
|
"MQTT": "MQTT",
|
||||||
|
"Console": "Console",
|
||||||
|
"About": "Informazioni DTU",
|
||||||
|
"Logout": "Esci",
|
||||||
|
"Login": "Login"
|
||||||
|
},
|
||||||
|
"base": {
|
||||||
|
"Loading": "Caricamento...",
|
||||||
|
"Reload": "Ricarica",
|
||||||
|
"Cancel": "Cancella",
|
||||||
|
"Save": "Salva",
|
||||||
|
"Refreshing": "Aggiorna",
|
||||||
|
"Pull": "Trascina in basso per aggiornare",
|
||||||
|
"Release": "Rilascia per aggiornare",
|
||||||
|
"Close": "Chiudi",
|
||||||
|
"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": "Scuro",
|
||||||
|
"Light": "Chiaro",
|
||||||
|
"Auto": "Automatico"
|
||||||
|
},
|
||||||
|
"apiresponse": {
|
||||||
|
"1001": "Settings saved!",
|
||||||
|
"1002": "No values found!",
|
||||||
|
"1003": "Data too large!",
|
||||||
|
"1004": "Failed to parse data!",
|
||||||
|
"1005": "Values are missing!",
|
||||||
|
"1006": "Write failed!",
|
||||||
|
"2001": "Serial cannot be zero!",
|
||||||
|
"2002": "Poll interval must be greater zero!",
|
||||||
|
"2003": "Invalid power level setting!",
|
||||||
|
"2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!",
|
||||||
|
"2005": "Invalid country selection!",
|
||||||
|
"3001": "Not deleted anything!",
|
||||||
|
"3002": "Configuration resettet. Rebooting now...",
|
||||||
|
"4001": "@:apiresponse.2001",
|
||||||
|
"4002": "Name must between 1 and {max} characters long!",
|
||||||
|
"4003": "Only {max} inverters are supported!",
|
||||||
|
"4004": "Inverter created!",
|
||||||
|
"4005": "Invalid ID specified!",
|
||||||
|
"4006": "Invalid amount of max channel setting given!",
|
||||||
|
"4007": "Inverter changed!",
|
||||||
|
"4008": "Inverter deleted!",
|
||||||
|
"4009": "Inverter order saved!",
|
||||||
|
"5001": "@:apiresponse.2001",
|
||||||
|
"5002": "Limit must between 1 and {max}!",
|
||||||
|
"5003": "Invalid type specified!",
|
||||||
|
"5004": "Invalid inverter specified!",
|
||||||
|
"6001": "Reboot triggered!",
|
||||||
|
"6002": "Reboot cancled!",
|
||||||
|
"7001": "MQTT Server must between 1 and {max} characters long!",
|
||||||
|
"7002": "Username must not longer then {max} characters!",
|
||||||
|
"7003": "Password must not longer then {max} characters!",
|
||||||
|
"7004": "Topic must not longer then {max} characters!",
|
||||||
|
"7005": "Topic must not contain space characters!",
|
||||||
|
"7006": "Topic must end with slash (/)!",
|
||||||
|
"7007": "Port must be a number between 1 and 65535!",
|
||||||
|
"7008": "Certificate must not longer then {max} characters!",
|
||||||
|
"7009": "LWT topic must not longer then {max} characters!",
|
||||||
|
"7010": "LWT topic must not contain space characters!",
|
||||||
|
"7011": "LWT online value must not longer then {max} characters!",
|
||||||
|
"7012": "LWT offline value must not longer then {max} characters!",
|
||||||
|
"7013": "Publish interval must be a number between {min} and {max}!",
|
||||||
|
"7014": "Hass topic must not longer then {max} characters!",
|
||||||
|
"7015": "Hass topic must not contain space characters!",
|
||||||
|
"7016": "LWT QOS must not greater then {max}!",
|
||||||
|
"7017": "Client ID must not longer then {max} characters!",
|
||||||
|
"8001": "IP address is invalid!",
|
||||||
|
"8002": "Netmask is invalid!",
|
||||||
|
"8003": "Gateway is invalid!",
|
||||||
|
"8004": "DNS Server IP 1 is invalid!",
|
||||||
|
"8005": "DNS Server IP 2 is invalid!",
|
||||||
|
"8006": "Administrative AccessPoint Timeout value is invalid",
|
||||||
|
"9001": "NTP Server must between 1 and {max} characters long!",
|
||||||
|
"9002": "Timezone must between 1 and {max} characters long!",
|
||||||
|
"9003": "Timezone description must between 1 and {max} characters long!",
|
||||||
|
"9004": "Year must be a number between {min} and {max}!",
|
||||||
|
"9005": "Month must be a number between {min} and {max}!",
|
||||||
|
"9006": "Day must be a number between {min} and {max}!",
|
||||||
|
"9007": "Hour must be a number between {min} and {max}!",
|
||||||
|
"9008": "Minute must be a number between {min} and {max}!",
|
||||||
|
"9009": "Second must be a number between {min} and {max}!",
|
||||||
|
"9010": "Time updated!",
|
||||||
|
"10001": "Password must between 8 and {max} characters long!",
|
||||||
|
"10002": "Authentication successful!",
|
||||||
|
"11001": "@:apiresponse.2001",
|
||||||
|
"11002": "@:apiresponse:5004",
|
||||||
|
"12001": "Profil must between 1 and {max} characters long!"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"LiveData": "Dati in tempo reale",
|
||||||
|
"SerialNumber": "Numero seriale: ",
|
||||||
|
"CurrentLimit": "Limite attuale: ",
|
||||||
|
"DataAge": "Aggiornamento Dati: ",
|
||||||
|
"Seconds": "{val} secondi",
|
||||||
|
"ShowSetInverterLimit": "Mostra / Imposta Limite di Potenza",
|
||||||
|
"TurnOnOff": "Accendi/Spegni Inverter",
|
||||||
|
"ShowInverterInfo": "Mostra info Inverter",
|
||||||
|
"ShowEventlog": "Mostra Log Eventi",
|
||||||
|
"UnreadMessages": "msg non letti",
|
||||||
|
"Loading": "@:base.Loading",
|
||||||
|
"EventLog": "Log Eventi",
|
||||||
|
"InverterInfo": "Info Inverter",
|
||||||
|
"LimitSettings": "Impostazioni Limite Potenza",
|
||||||
|
"LastLimitSetStatus": "Stato ultimo limite impostato:",
|
||||||
|
"SetLimit": "Imposta Limite a:",
|
||||||
|
"Relative": "Percentuale (%)",
|
||||||
|
"Absolute": "Assoluto (W)",
|
||||||
|
"LimitHint": "<b>Nota:</b> Se imposti il limite assoluto, il valore sul display sarà aggiornato dopo circa 4 minuti.",
|
||||||
|
"SetPersistent": "Imposta Limite in Modo Persistente",
|
||||||
|
"SetNonPersistent": "Imposta Limite Temporaneamente",
|
||||||
|
"PowerSettings": "Impostazioni Potenza",
|
||||||
|
"LastPowerSetStatus": "Ultimo Stato dell'Inverter:",
|
||||||
|
"TurnOn": "Accendi Inverter",
|
||||||
|
"TurnOff": "Spegni Inverter",
|
||||||
|
"Restart": "Riavvia Inverter",
|
||||||
|
"Failure": "Fallito",
|
||||||
|
"Pending": "In Attesa",
|
||||||
|
"Ok": "Ok",
|
||||||
|
"Unknown": "Sconosciuto",
|
||||||
|
"ShowGridProfile": "Mostra Settaggi Inverter",
|
||||||
|
"GridProfile": "Settaggi Inverter",
|
||||||
|
"LoadingInverter": "In attesa dei dati... (puo' richiedere fino a 10 secondi)",
|
||||||
|
"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": "Inizio",
|
||||||
|
"Stop": "Fine",
|
||||||
|
"Id": "ID",
|
||||||
|
"Message": "Messaggio"
|
||||||
|
},
|
||||||
|
"devinfo": {
|
||||||
|
"NoInfo": "Informazioni non disponibili",
|
||||||
|
"NoInfoLong": "Ancora nessuna informazione dall'inverter. Sto riprovando...",
|
||||||
|
"UnknownModel": "Modello sconosciuto! Per favore fornisci \"Hardware Part Number\" ed il modello (esempio HM-350) in una Issue su <a href=\"https://github.com/tbnobody/OpenDTU/issues\" target=\"_blank\">GitHub</a>.",
|
||||||
|
"Serial": "Seriale",
|
||||||
|
"ProdYear": "Produzione Annua",
|
||||||
|
"ProdWeek": "Produzione Settimanale",
|
||||||
|
"Model": "Modello",
|
||||||
|
"DetectedMaxPower": "Rilevata potenza massima",
|
||||||
|
"BootloaderVersion": "Versione Bootloader",
|
||||||
|
"FirmwareVersion": "Versione Firmware",
|
||||||
|
"FirmwareBuildDate": "Data Firmware",
|
||||||
|
"HardwarePartNumber": "Hardware Part Number",
|
||||||
|
"HardwareVersion": "Hardware Version",
|
||||||
|
"SupportsPowerDistributionLogic": "'Power Distribution Logic' supported",
|
||||||
|
"Yes": "@:base.Yes",
|
||||||
|
"No": "@:base.No"
|
||||||
|
},
|
||||||
|
"gridprofile": {
|
||||||
|
"NoInfo": "@:devinfo.NoInfo",
|
||||||
|
"NoInfoLong": "@:devinfo.NoInfoLong",
|
||||||
|
"Name": "Nome",
|
||||||
|
"Version": "Versione",
|
||||||
|
"Enabled": "@:wifistationinfo.Enabled",
|
||||||
|
"Disabled": "@:wifistationinfo.Disabled",
|
||||||
|
"GridprofileSupport": "Supporto sviluppatori",
|
||||||
|
"GridprofileSupportLong": "Clicca <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">qui</a> per ulteriori informazioni."
|
||||||
|
},
|
||||||
|
"systeminfo": {
|
||||||
|
"SystemInfo": "Info Sistema",
|
||||||
|
"VersionError": "Errore ricezione della versione",
|
||||||
|
"VersionNew": "Nuova versione disponibile! Mostra aggiornamenti!",
|
||||||
|
"VersionOk": "Già aggiornato!"
|
||||||
|
},
|
||||||
|
"firmwareinfo": {
|
||||||
|
"FirmwareInformation": "Info Firmware",
|
||||||
|
"Hostname": "Hostname",
|
||||||
|
"SdkVersion": "SDK Version",
|
||||||
|
"ConfigVersion": "Config Version",
|
||||||
|
"FirmwareVersion": "Firmware Version / Git Hash",
|
||||||
|
"PioEnv": "PIO Environment",
|
||||||
|
"FirmwareVersionHint": "Click here to show information about your current version",
|
||||||
|
"FirmwareUpdate": "Firmware Update",
|
||||||
|
"FirmwareUpdateHint": "Click here to view the changes between your version and the latest version",
|
||||||
|
"FrmwareUpdateAllow": "By activating the update check, a request is sent to GitHub.com each time the page is called up to retrieve the currently available version. If you do not agree with this, leave this function deactivated.",
|
||||||
|
"ResetReason0": "Reset Reason CPU 0",
|
||||||
|
"ResetReason1": "Reset Reason CPU 1",
|
||||||
|
"ConfigSaveCount": "Config save count",
|
||||||
|
"Uptime": "Uptime",
|
||||||
|
"UptimeValue": "0 days {time} | 1 day {time} | {count} days {time}"
|
||||||
|
},
|
||||||
|
"hardwareinfo": {
|
||||||
|
"HardwareInformation": "Info Hardware",
|
||||||
|
"ChipModel": "Chip Model",
|
||||||
|
"ChipRevision": "Chip Revision",
|
||||||
|
"ChipCores": "Chip Cores",
|
||||||
|
"CpuFrequency": "CPU Frequency",
|
||||||
|
"Mhz": "MHz",
|
||||||
|
"CpuTemperature": "CPU Temperature",
|
||||||
|
"FlashSize": "Flash Memory Size"
|
||||||
|
},
|
||||||
|
"memoryinfo": {
|
||||||
|
"MemoryInformation": "Info Memoria",
|
||||||
|
"Type": "Tipo",
|
||||||
|
"Usage": "Uso",
|
||||||
|
"Free": "Libera",
|
||||||
|
"Used": "Usata",
|
||||||
|
"Size": "Dimensione",
|
||||||
|
"Heap": "Heap",
|
||||||
|
"PsRam": "PSRAM",
|
||||||
|
"LittleFs": "LittleFs",
|
||||||
|
"Sketch": "Sketch"
|
||||||
|
},
|
||||||
|
"heapdetails": {
|
||||||
|
"HeapDetails": "Dettagli memoria Heap",
|
||||||
|
"TotalFree": "Libera totale",
|
||||||
|
"LargestFreeBlock": "Blocco contiguo libero più grande",
|
||||||
|
"MaxUsage": "Massima utilizzata dall'avvio",
|
||||||
|
"Fragmentation": "Livello frammentazione"
|
||||||
|
},
|
||||||
|
"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": "Info Transceiver Radio",
|
||||||
|
"Status": "{module} Stato",
|
||||||
|
"ChipStatus": "{module} Chip Stato",
|
||||||
|
"ChipType": "{module} Chip Tipo",
|
||||||
|
"Connected": "connesso",
|
||||||
|
"NotConnected": "non connesso",
|
||||||
|
"Configured": "configurato",
|
||||||
|
"NotConfigured": "no configurato",
|
||||||
|
"Unknown": "Sconosciuto"
|
||||||
|
},
|
||||||
|
"networkinfo": {
|
||||||
|
"NetworkInformation": "Informazioni Rete"
|
||||||
|
},
|
||||||
|
"wifistationinfo": {
|
||||||
|
"WifiStationInfo": "Info WiFi (Station)",
|
||||||
|
"Status": "Stato",
|
||||||
|
"Enabled": "abilitato",
|
||||||
|
"Disabled": "disabilitato",
|
||||||
|
"Ssid": "SSID",
|
||||||
|
"Bssid": "BSSID",
|
||||||
|
"Quality": "Qualità",
|
||||||
|
"Rssi": "RSSI"
|
||||||
|
},
|
||||||
|
"wifiapinfo": {
|
||||||
|
"WifiApInfo": "Info WiFi (Access Point)",
|
||||||
|
"Status": "@:wifistationinfo.Status",
|
||||||
|
"Enabled": "@:wifistationinfo.Enabled",
|
||||||
|
"Disabled": "@:wifistationinfo.Disabled",
|
||||||
|
"Ssid": "@:wifistationinfo.Ssid",
|
||||||
|
"Stations": "Numero Stazioni"
|
||||||
|
},
|
||||||
|
"interfacenetworkinfo": {
|
||||||
|
"NetworkInterface": "Interfaccia di Rete ({iface})",
|
||||||
|
"Hostname": "@:firmwareinfo.Hostname",
|
||||||
|
"IpAddress": "Indirizzo IP",
|
||||||
|
"Netmask": "Netmask",
|
||||||
|
"DefaultGateway": "Gateway",
|
||||||
|
"Dns": "DNS {num}",
|
||||||
|
"MacAddress": "Indirizzo MAC"
|
||||||
|
},
|
||||||
|
"interfaceapinfo": {
|
||||||
|
"NetworkInterface": "Interfaccia di Rete (Access Point)",
|
||||||
|
"IpAddress": "@:interfacenetworkinfo.IpAddress",
|
||||||
|
"MacAddress": "@:interfacenetworkinfo.MacAddress"
|
||||||
|
},
|
||||||
|
"ntpinfo": {
|
||||||
|
"NtpInformation": "Informazioni NTP",
|
||||||
|
"ConfigurationSummary": "Riepilogo Configurazione",
|
||||||
|
"Server": "Server",
|
||||||
|
"Timezone": "Timezone",
|
||||||
|
"TimezoneDescription": "Descrizione Timezone",
|
||||||
|
"CurrentTime": "Data/Ora attuale",
|
||||||
|
"Status": "Stato",
|
||||||
|
"Synced": "sincronizzata",
|
||||||
|
"NotSynced": "non sincronizzata",
|
||||||
|
"LocalTime": "Ora Locale",
|
||||||
|
"Sunrise": "Alba",
|
||||||
|
"Sunset": "Tramonto",
|
||||||
|
"NotAvailable": "Non Disponibile",
|
||||||
|
"Mode": "Modalità",
|
||||||
|
"Day": "Giorno",
|
||||||
|
"Night": "Notte"
|
||||||
|
},
|
||||||
|
"mqttinfo": {
|
||||||
|
"MqttInformation": "Informazioni MQTT",
|
||||||
|
"ConfigurationSummary": "@:ntpinfo.ConfigurationSummary",
|
||||||
|
"Status": "@:ntpinfo.Status",
|
||||||
|
"Enabled": "Abilitato",
|
||||||
|
"Disabled": "Disabilitato",
|
||||||
|
"Server": "@:ntpinfo.Server",
|
||||||
|
"Port": "Porta",
|
||||||
|
"ClientId": "Client ID",
|
||||||
|
"Username": "Username",
|
||||||
|
"BaseTopic": "Topic Base",
|
||||||
|
"PublishInterval": "Intervallo Publish",
|
||||||
|
"Seconds": "{sec} secondi",
|
||||||
|
"CleanSession": "CleanSession",
|
||||||
|
"Retain": "Retain",
|
||||||
|
"Tls": "TLS",
|
||||||
|
"RootCertifcateInfo": "Info Certificato Root CA",
|
||||||
|
"TlsCertLogin": "Entra con Certificato TLS",
|
||||||
|
"ClientCertifcateInfo": "Info Certificato Client",
|
||||||
|
"HassSummary": "Riepilogo Configurazione Home Assistant MQTT Auto Discovery",
|
||||||
|
"Expire": "Scade",
|
||||||
|
"IndividualPanels": "Pannello Individuale",
|
||||||
|
"RuntimeSummary": "Riepilogo Runtime",
|
||||||
|
"ConnectionStatus": "Stato Connessione",
|
||||||
|
"Connected": "connesso",
|
||||||
|
"Disconnected": "disconnesso"
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"Console": "Console",
|
||||||
|
"VirtualDebugConsole": "Virtual Debug Console",
|
||||||
|
"EnableAutoScroll": "Abilita AutoScroll",
|
||||||
|
"ClearConsole": "Pulisci Console",
|
||||||
|
"CopyToClipboard": "Copia nella clipboard"
|
||||||
|
},
|
||||||
|
"inverterchannelinfo": {
|
||||||
|
"String": "Stringa {num}",
|
||||||
|
"Phase": "Fase {num}",
|
||||||
|
"General": "Generale"
|
||||||
|
},
|
||||||
|
"invertertotalinfo": {
|
||||||
|
"TotalYieldTotal": "Totale Energia",
|
||||||
|
"TotalYieldDay": "Energia Giornaliera",
|
||||||
|
"TotalPower": "Potenza Totale"
|
||||||
|
},
|
||||||
|
"inverterchannelproperty": {
|
||||||
|
"Power": "Potenza",
|
||||||
|
"Voltage": "Tensione",
|
||||||
|
"Current": "Corrente",
|
||||||
|
"Power DC": "PotenzaDC",
|
||||||
|
"YieldDay": "EnergiaOggi",
|
||||||
|
"YieldTotal": "EnergiaTotale",
|
||||||
|
"Frequency": "Frequenza",
|
||||||
|
"Temperature": "Temperatura",
|
||||||
|
"PowerFactor": "FattorePotenza",
|
||||||
|
"ReactivePower": "PotenzaReattiva",
|
||||||
|
"Efficiency": "Efficienza",
|
||||||
|
"Irradiation": "Irragiamento"
|
||||||
|
},
|
||||||
|
"maintenancereboot": {
|
||||||
|
"DeviceReboot": "Riavvio DTU",
|
||||||
|
"PerformReboot": "Fai il riavvio",
|
||||||
|
"Reboot": "Riavvio!",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"RebootOpenDTU": "Riavvio OpenDTU",
|
||||||
|
"RebootQuestion": "Vuoi veramente riavvia il DTU?",
|
||||||
|
"RebootHint": "<b>Nota:</b> Normalmente non serve riavviare OpenDTU, in quanto esegue automaticamente il ravvio quando necessario (ad esempio dopo aggiornamento firmware). Modifiche alla configurazione vengono apprese subito, senza richiedere riavvio. Se devi riavviare a causa di un errore, ti preghiamo di segnalarcelo cliccando su <a href=\"https://github.com/tbnobody/OpenDTU/issues\" class=\"alert-link\" target=\"_blank\">https://github.com/tbnobody/OpenDTU/issues</a>."
|
||||||
|
},
|
||||||
|
"dtuadmin": {
|
||||||
|
"DtuSettings": "Impostazioni DTU",
|
||||||
|
"DtuConfiguration": "Configurazione DTU",
|
||||||
|
"Serial": "Seriale",
|
||||||
|
"SerialHint": "Sia il DTU che l'inverter hanno un numero seriale. Il numero seriale del DTU è generato casualmente al primo avvio e normalmente non serve modificarlo.",
|
||||||
|
"PollInterval": "Intervallo Interrogazione",
|
||||||
|
"Seconds": "Secondi",
|
||||||
|
"NrfPaLevel": "Potenza Trasmettitore NRF24",
|
||||||
|
"CmtPaLevel": "Potenza Trasmettitore CMT2300A",
|
||||||
|
"NrfPaLevelHint": "Usato per inverter HM. Considera che aumentando la potenza aumentano il consumo di corrente.",
|
||||||
|
"CmtPaLevelHint": "Usato per inverter HMS/HMT. Considera che aumentando la potenza aumentano il consumo di corrente.",
|
||||||
|
"CmtCountry": "CMT2300A Zona/Paese",
|
||||||
|
"CmtCountryHint": "Ogni zona ha una differente allocazione di frequenze utilizzabili.",
|
||||||
|
"country_0": "Europa ({min}MHz - {max}MHz)",
|
||||||
|
"country_1": "Nord America ({min}MHz - {max}MHz)",
|
||||||
|
"country_2": "Brasile ({min}MHz - {max}MHz)",
|
||||||
|
"CmtFrequency": "Frequenza CMT2300A",
|
||||||
|
"CmtFrequencyHint": "Fai attenzione ad usare solo frequenze ammesse nel tuo Paese! Dopo la modifica frequenza, servono fino a 15 minuti affinché la connessione si ristabilisca.",
|
||||||
|
"CmtFrequencyWarning": "La frequenza selezionata è fuori dal range selezionato dal tuo Paese. Verifica che la frequenza selezionata non violi le normative del tuo Paese.",
|
||||||
|
"MHz": "{mhz} MHz",
|
||||||
|
"dBm": "{dbm} dBm",
|
||||||
|
"Min": "Minima ({db} dBm)",
|
||||||
|
"Low": "Bassa ({db} dBm)",
|
||||||
|
"High": "Alta ({db} dBm)",
|
||||||
|
"Max": "Massima ({db} dBm)"
|
||||||
|
},
|
||||||
|
"securityadmin": {
|
||||||
|
"SecuritySettings": "Impostazioni di Sicurezza",
|
||||||
|
"AdminPassword": "Password Admin",
|
||||||
|
"Password": "Password",
|
||||||
|
"RepeatPassword": "Ripeti Password",
|
||||||
|
"PasswordHint": "<b>Nota:</b> La password di amministrazione viene utilizzata non solo per accedere a questa interfaccia web (con user 'admin'), ma anche per connettersi al dispositivo in modalità AP. Deve avere da 8 a 64 caratteri.",
|
||||||
|
"Permissions": "Permessi",
|
||||||
|
"ReadOnly": "Permetti accessi web in sola lettura senza richiedere la password"
|
||||||
|
},
|
||||||
|
"ntpadmin": {
|
||||||
|
"NtpSettings": "Impostazioni NTP (Data / Ora)",
|
||||||
|
"NtpConfiguration": "Configurazione NTP",
|
||||||
|
"TimeServer": "Server NTP",
|
||||||
|
"TimeServerHint": "Puoi lasciare il valore di default, nel caso in cui OpenDTU abbia accesso ad internet.",
|
||||||
|
"Timezone": "Timezone",
|
||||||
|
"TimezoneConfig": "Timezone Config",
|
||||||
|
"LocationConfiguration": "Configurazione Posizione",
|
||||||
|
"Longitude": "Longitudine",
|
||||||
|
"Latitude": "Latitudine",
|
||||||
|
"SunSetType": "Tipo di Alba",
|
||||||
|
"SunSetTypeHint": "Influenza il calcolo dell'ora di Alba/Tramonto. Dopo la conferma, è richiesto fino ad un minuto perché la modifica venga applicata.",
|
||||||
|
"OFFICIAL": "Standard dawn (90.8°)",
|
||||||
|
"NAUTICAL": "Nautical dawn (102°)",
|
||||||
|
"CIVIL": "Civil dawn (96°)",
|
||||||
|
"ASTONOMICAL": "Astronomical dawn (108°)",
|
||||||
|
"ManualTimeSynchronization": "Sincronizzazione Manuale Data/Ora",
|
||||||
|
"CurrentOpenDtuTime": "Ora OpenDTU attuale",
|
||||||
|
"CurrentLocalTime": "Ora Locale attuale",
|
||||||
|
"SynchronizeTime": "Sincronizza Data/Ora",
|
||||||
|
"SynchronizeTimeHint": "<b>Nota:</b> Puoi usare la sincronizzazione manuale per impostare Data/Ora nel caso che non sia disponibile un server NTP. In questo caso la data/ora viene persa in caso di mancata alimentazione. Inoltre, con la sincronizzazione manuale ci sarà una progressiva deriva della Data/Ora in quanto l'ESP32 non ha un Real Time Clock interno."
|
||||||
|
},
|
||||||
|
"networkadmin": {
|
||||||
|
"NetworkSettings": "Impostazioni di Rete",
|
||||||
|
"WifiConfiguration": "Configurazione WiFi",
|
||||||
|
"WifiSsid": "WiFi SSID",
|
||||||
|
"WifiPassword": "WiFi Password",
|
||||||
|
"Hostname": "Hostname",
|
||||||
|
"HostnameHint": "<b>Nota:</b> Il testo <span class=\"font-monospace\">%06X</span> sarà rimpiazzato con le ultime 6 cifre del ChipID dell'ESP32 in formato esadecimale.",
|
||||||
|
"EnableDhcp": "Abilita DHCP",
|
||||||
|
"StaticIpConfiguration": "Configurazione IP Statico",
|
||||||
|
"IpAddress": "Indirizzo IP",
|
||||||
|
"Netmask": "Netmask",
|
||||||
|
"DefaultGateway": "Default Gateway",
|
||||||
|
"Dns": "DNS Server {num}",
|
||||||
|
"AdminAp": "Configurazione WiFi (Admin AccessPoint)",
|
||||||
|
"ApTimeout": "Timeout AccessPoint",
|
||||||
|
"ApTimeoutHint": "Tempo in cui la modalità AccessPoint rimarrà attiva. 0=per sempre.",
|
||||||
|
"Minutes": "minuti",
|
||||||
|
"EnableMdns": "Abilita mDNS",
|
||||||
|
"MdnsSettings": "Configurazione mDNS"
|
||||||
|
},
|
||||||
|
"mqttadmin": {
|
||||||
|
"MqttSettings": "Impostazioni MQTT",
|
||||||
|
"MqttConfiguration": "Configurazione MQTT",
|
||||||
|
"EnableMqtt": "Abilita MQTT",
|
||||||
|
"EnableHass": "Abilita Home Assistant MQTT Auto Discovery",
|
||||||
|
"MqttBrokerParameter": "Parametri Broker MQTT",
|
||||||
|
"Hostname": "Hostname",
|
||||||
|
"HostnameHint": "Hostname o Indirizzo IP",
|
||||||
|
"Port": "Porta",
|
||||||
|
"ClientId": "Client ID",
|
||||||
|
"Username": "Username",
|
||||||
|
"UsernameHint": "Username, lascia vuoto per connessione anonima",
|
||||||
|
"Password": "Password",
|
||||||
|
"PasswordHint": "Password, lascia vuota per connessione anonima",
|
||||||
|
"BaseTopic": "Topic Base",
|
||||||
|
"BaseTopicHint": "Topic Base, prefisso da aggiungere (ad esempio inverter/)",
|
||||||
|
"PublishInterval": "Intervallo pubblicazione",
|
||||||
|
"Seconds": "secondi",
|
||||||
|
"CleanSession": "Abilita CleanSession",
|
||||||
|
"EnableRetain": "Abilita Retain",
|
||||||
|
"EnableTls": "Abilita TLS",
|
||||||
|
"RootCa": "CA-Root-Certificate (default Letsencrypt)",
|
||||||
|
"TlsCertLoginEnable": "Abilita Login con certificato TLS",
|
||||||
|
"ClientCert": "TLS Client-Certificate",
|
||||||
|
"ClientKey": "TLS Client-Key",
|
||||||
|
"LwtParameters": "Parametri LWT",
|
||||||
|
"LwtTopic": "Topic LWT",
|
||||||
|
"LwtTopicHint": "Topic LWT, da aggiungere al Topic Base",
|
||||||
|
"LwtOnline": "Messaggio 'Online0 LWT",
|
||||||
|
"LwtOnlineHint": "Messaggio pubblicato quando online",
|
||||||
|
"LwtOffline": "Messaggio 'Offline' LWT",
|
||||||
|
"LwtOfflineHint": "Messaggio che sarà pubblicato quando offline",
|
||||||
|
"LwtQos": "QoS (Quality of Service)",
|
||||||
|
"QOS0": "0 (Al massimo una volta)",
|
||||||
|
"QOS1": "1 (Almeno una volta)",
|
||||||
|
"QOS2": "2 (Esattamente una volta)",
|
||||||
|
"HassParameters": "Parametri Home Assistant MQTT Auto Discovery",
|
||||||
|
"HassPrefixTopic": "Prefisso Topic",
|
||||||
|
"HassPrefixTopicHint": "Prefisso per Topic autodiscovery",
|
||||||
|
"HassRetain": "Abilita Retain",
|
||||||
|
"HassExpire": "Abilita Scadenza",
|
||||||
|
"HassIndividual": "Pannelli Individuale"
|
||||||
|
},
|
||||||
|
"inverteradmin": {
|
||||||
|
"InverterSettings": "Impostazioni Inverter",
|
||||||
|
"AddInverter": "Aggiungi nuovo Inverter",
|
||||||
|
"Serial": "Seriale",
|
||||||
|
"Name": "Nome",
|
||||||
|
"Add": "Aggiungi",
|
||||||
|
"AddHint": "<b>Nota:</b> Potrai aggiungere ulteriori parametri dopo aver creato l'inverter, cliccando sull'icona 'Matita' nella lista inverter.",
|
||||||
|
"InverterList": "Lista Inverter",
|
||||||
|
"Status": "Stato",
|
||||||
|
"Send": "Invia",
|
||||||
|
"Receive": "Riceve",
|
||||||
|
"StatusHint": "<b>Nota:</b> L'inverter viene alimentato dal fotovoltaico. Durante la notte, l'inverter risulterà spento. Le richieste potranno comunque essere trasmesse.",
|
||||||
|
"Type": "Tipo",
|
||||||
|
"Action": "Azione",
|
||||||
|
"SaveOrder": "Salva ordine",
|
||||||
|
"DeleteInverter": "Rimuovi inverter",
|
||||||
|
"EditInverter": "Modifica inverter",
|
||||||
|
"General": "Generale",
|
||||||
|
"String": "Stringa",
|
||||||
|
"Advanced": "Avanzate",
|
||||||
|
"InverterSerial": "Seriale Inverter:",
|
||||||
|
"InverterName": "Nome Inverter:",
|
||||||
|
"InverterNameHint": "Puoi specificare un nome qualsiasi da assegnare all'inverter.",
|
||||||
|
"InverterStatus": "Riceve / Invia",
|
||||||
|
"PollEnable": "Interroga inverter",
|
||||||
|
"PollEnableNight": "Interroga inverter di notte",
|
||||||
|
"CommandEnable": "Invia comandi",
|
||||||
|
"CommandEnableNight": "Invia comandi di notte",
|
||||||
|
"StringName": "Nome stringa {num}:",
|
||||||
|
"StringNameHint": "Qui puoi specificare un nome qualsiasi per la porta dell'inverter o per il pannello fotovoltaico collegato.",
|
||||||
|
"StringMaxPower": "Massima potenza stringa {num}:",
|
||||||
|
"StringMaxPowerHint": "Inserisci la potenza massima associata ai panelli fotovoltaici collegati a questa stringa.",
|
||||||
|
"StringYtOffset": "Offset Energia totale per la stringa {num}:",
|
||||||
|
"StringYtOffsetHint": "Questo offset viene utilizzato per azzerare il contatore qualora venga usato un inverter usato.",
|
||||||
|
"InverterHint": "*) Inserisci la potenza W<sub>p</sub> dei pannelli fotovoltaici collegati alla stringa: servirà per calcolare l'irragiamento.",
|
||||||
|
"ReachableThreshold": "Reachable Threshold",
|
||||||
|
"ReachableThresholdHint": "Definisce il numero di richieste fallite prima che l'inverter sia considerato irraggiungibile.",
|
||||||
|
"ZeroRuntime": "Azzera dati in tempo reale",
|
||||||
|
"ZeroRuntimeHint": "Azzera i dati in tempo reale (tranne l'Energia) se l'inverter diventa irraggiunbile.",
|
||||||
|
"ZeroDay": "Azzera dati energia alla mezzanotte",
|
||||||
|
"ZeroDayHint": "Questo vale se l'inverter risulta irraggiungibile. Se l'inverter risponde anche di notte, verranno mostrati i suoi valori. (Il Reset si verifica al riavvio)",
|
||||||
|
"ClearEventlog": "Clear Eventlog at midnight",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"Save": "@:base.Save",
|
||||||
|
"DeleteMsg": "Sicuro di voler rimuovere l'inverter \"{name}\" con numero seriale {serial}?",
|
||||||
|
"Delete": "Rimuovi",
|
||||||
|
"YieldDayCorrection": "Correzione energia giornaliera",
|
||||||
|
"YieldDayCorrectionHint": "Aggiungi questo valore all'energia giornaliera se l'inverter è stato riavviato. Questo valore sarò resettato a mezzanotte"
|
||||||
|
},
|
||||||
|
"fileadmin": {
|
||||||
|
"ConfigManagement": "Configurazione Gestione",
|
||||||
|
"BackupHeader": "Backup: Configurazione File Backup",
|
||||||
|
"BackupConfig": "Esegui il backup del file",
|
||||||
|
"Backup": "Backup",
|
||||||
|
"Restore": "Ripristina",
|
||||||
|
"NoFileSelected": "Nessun file selezionato",
|
||||||
|
"RestoreHeader": "Ripristina: Ripristina File Configurazione",
|
||||||
|
"Back": "Indietro",
|
||||||
|
"UploadSuccess": "Invio File con successo",
|
||||||
|
"RestoreHint": "<b>Nota:</b> questa operazione rimpiazza la configurazione con quella contenuta nel file, e poi riavvia automaticamente OpenDTU per applicare la nuova configurazione.",
|
||||||
|
"ResetHeader": "Inizializza: Esegui il Factory Reset",
|
||||||
|
"FactoryResetButton": "Ripristina Configurazione Factory-Default",
|
||||||
|
"ResetHint": "<b>Nota:</b> Clicca 'Ripristina Configurazione Factory-Default' per stabilire le impostazioni di fabbrica e riavviare automaticamente OpenDTU.",
|
||||||
|
"FactoryReset": "Factory Reset",
|
||||||
|
"ResetMsg": "Sei sicuro di voler cancellare la configurazione attuale e applicare la configurazione di fabbrica?",
|
||||||
|
"ResetConfirm": "Factory Reset!",
|
||||||
|
"Cancel": "@:base.Cancel",
|
||||||
|
"InvalidJson": "JSON file is formatted incorrectly.",
|
||||||
|
"InvalidJsonContent": "JSON file has the wrong content."
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"Login": "Login",
|
||||||
|
"SystemLogin": "System Login",
|
||||||
|
"Username": "Username",
|
||||||
|
"UsernameRequired": "Inserisci Username",
|
||||||
|
"Password": "Password",
|
||||||
|
"PasswordRequired": "Inserisci Password",
|
||||||
|
"LoginButton": "Login"
|
||||||
|
},
|
||||||
|
"firmwareupgrade": {
|
||||||
|
"FirmwareUpgrade": "Aggiornamento Firmware",
|
||||||
|
"Loading": "@:base.Loading",
|
||||||
|
"OtaError": "Errore aggiornamento OTA",
|
||||||
|
"Back": "Indietro",
|
||||||
|
"Retry": "Riprova",
|
||||||
|
"OtaStatus": "Stato OTA",
|
||||||
|
"OtaSuccess": "Aggiornamento firmware eseguito con successo. Il dispositivo si riavvierà automaticamente. Quando sarà nuovamente disponibile, l'interfacca sarà ricaricata automaticamente.",
|
||||||
|
"FirmwareUpload": "Invia Firmware",
|
||||||
|
"UploadProgress": "Upload in corso"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"AboutOpendtu": "About OpenDTU",
|
||||||
|
"Documentation": "Documentazione",
|
||||||
|
"DocumentationBody": "La documentazione firmware e hardware sono disponibili qui: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
|
||||||
|
"ProjectOrigin": "Origine Progetto",
|
||||||
|
"ProjectOriginBody1": "Questo progetto è partito da <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">questa discussione. (Mikrocontroller.net)</a>",
|
||||||
|
"ProjectOriginBody2": "Il protocollo Hoymiles è stato decriptato grazie al contributo volontario di molti programmatori. OpenDTU, fra gli altri, è stato sviluppato grazie a questo lavoro. Il progetto è distribuito con Licenza Open Source (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",
|
||||||
|
"ProjectOriginBody3": "Il software è stato sviluppato con le nostre migliori conoscenze e convinzioni. Tuttavia, non si assume alcuna responsabilità per malfunzionamenti o perdita di garanzia dell'inverter.",
|
||||||
|
"ProjectOriginBody4": "OpenDTU è disponibile gratuitamente. Se hai pagato per questo software, probabilmente sei stato truffato.",
|
||||||
|
"NewsUpdates": "Novità e Aggiornamenti",
|
||||||
|
"NewsUpdatesBody": "Nuovi aggiornamenti sono disponibili su Github: <a href=\"https://github.com/tbnobody/OpenDTU\" target=\"_blank\">https://github.com/tbnobody/OpenDTU</a>",
|
||||||
|
"ErrorReporting": "Segnalazione Errori",
|
||||||
|
"ErrorReportingBody": "Per favore segnala eventuali problemi utilizzando le funzionalità della piattaforma <a href=\"https://github.com/tbnobody/OpenDTU/issues\" target=\"_blank\">Github</a>",
|
||||||
|
"Discussion": "Discussioni",
|
||||||
|
"DiscussionBody": "Puoi avviare una discussione con noi su <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": "Non è possibile dialogare con il modulo radio selezionato. Controlla i collegamenti alla radio.",
|
||||||
|
"TimeSync": "La Data/Ora non sono state sincronizzate, ed in tal caso non è possibile eseguire richieste all'inverter. Questa condizione è normale appena avviato, tuttavia dopo un po' (>1 minuto), questa situazione potrebbe indicare un problema di accesso al server NTP.",
|
||||||
|
"TimeSyncLink": "Controlla le impostazioni Data/Ora.",
|
||||||
|
"DefaultPassword": "Stai usando la password di default per accedere all'interfaccia web e per la modalità Access Point di emergenza. Questo può portare ad un rischio di sicurezza.",
|
||||||
|
"DefaultPasswordLink": "Per favore cambia la password.",
|
||||||
|
"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": "Device-Manager",
|
||||||
|
"ParseError": "Parse error in 'pin_mapping.json': {error}",
|
||||||
|
"PinAssignment": "Impostazioni Connessione",
|
||||||
|
"SelectedProfile": "Profilo selezionato",
|
||||||
|
"DefaultProfile": "(Impostazioni di Default)",
|
||||||
|
"ProfileHint": "Il tuo dispositivo potrebbe smettere di rispondere selezionando un profilo incompatibile. In questo caso, dovrai eseguire una cancellazione collegandoti all'interfaccia seriale.",
|
||||||
|
"Display": "Display",
|
||||||
|
"PowerSafe": "Abilita Risparmio Energetico",
|
||||||
|
"PowerSafeHint": "Spegni il display se l'inverter non produce.",
|
||||||
|
"Screensaver": "Abilita Screensaver",
|
||||||
|
"ScreensaverHint": "Muove il testo nel display per prevenire danneggiamento pixel. (Utile in caso di display OLED)",
|
||||||
|
"DiagramMode": "Modalità grafica",
|
||||||
|
"off": "Off",
|
||||||
|
"small": "Small",
|
||||||
|
"fullscreen": "Fullscreen",
|
||||||
|
"DiagramDuration": "Durata grafico",
|
||||||
|
"DiagramDurationHint": "Periodo che viene mostrato nel grafico.",
|
||||||
|
"Seconds": "Secondi",
|
||||||
|
"Contrast": "Contrasto ({contrast})",
|
||||||
|
"Rotation": "Rotazione",
|
||||||
|
"rot0": "Nessuna rotazione",
|
||||||
|
"rot90": "Rotazione 90 gradi",
|
||||||
|
"rot180": "Rotazione 180 gradi",
|
||||||
|
"rot270": "Rotazione 270 gradi",
|
||||||
|
"DisplayLanguage": "Linuga Display",
|
||||||
|
"en": "English",
|
||||||
|
"de": "German",
|
||||||
|
"fr": "French",
|
||||||
|
"Leds": "LEDs",
|
||||||
|
"EqualBrightness": "Equalizza luminosità",
|
||||||
|
"LedBrightness": "LED {led}, Luminosità ({brightness})"
|
||||||
|
},
|
||||||
|
"pininfo": {
|
||||||
|
"Category": "Categoria",
|
||||||
|
"Name": "Nome",
|
||||||
|
"Number": "Numero",
|
||||||
|
"ValueSelected": "Selezionato",
|
||||||
|
"ValueActive": "Attivo"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,142 +0,0 @@
|
|||||||
#include "cmt_spi3.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <driver/spi_master.h>
|
|
||||||
#include <esp_rom_gpio.h> // for esp_rom_gpio_connect_out_signal
|
|
||||||
|
|
||||||
SemaphoreHandle_t paramLock = NULL;
|
|
||||||
#define SPI_PARAM_LOCK() \
|
|
||||||
do { \
|
|
||||||
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
|
||||||
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
|
||||||
|
|
||||||
// for ESP32 this is the so-called HSPI
|
|
||||||
// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore,
|
|
||||||
// it is simply the first externally usable hardware SPI master controller
|
|
||||||
#define SPI_CMT SPI2_HOST
|
|
||||||
|
|
||||||
spi_device_handle_t spi_reg, spi_fifo;
|
|
||||||
|
|
||||||
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed)
|
|
||||||
{
|
|
||||||
paramLock = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
spi_bus_config_t buscfg = {
|
|
||||||
.mosi_io_num = pin_sdio,
|
|
||||||
.miso_io_num = -1, // single wire MOSI/MISO
|
|
||||||
.sclk_io_num = pin_clk,
|
|
||||||
.quadwp_io_num = -1,
|
|
||||||
.quadhd_io_num = -1,
|
|
||||||
.max_transfer_sz = 32,
|
|
||||||
};
|
|
||||||
spi_device_interface_config_t devcfg = {
|
|
||||||
.command_bits = 1,
|
|
||||||
.address_bits = 7,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 1,
|
|
||||||
.cs_ena_posttrans = 1,
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_cs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED));
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg));
|
|
||||||
|
|
||||||
// FiFo
|
|
||||||
spi_device_interface_config_t devcfg2 = {
|
|
||||||
.command_bits = 0,
|
|
||||||
.address_bits = 0,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 2,
|
|
||||||
.cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_fcs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo));
|
|
||||||
|
|
||||||
esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false);
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write(const uint8_t addr, const uint8_t dat)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
tx_data = ~dat;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 1,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data,
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t cmt_spi3_read(const uint8_t addr)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 0,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
return rx_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data, // reference to write data
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
tx_data = ~buf[i]; // negate buffer contents
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
buf[i] = rx_data;
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include "cmt_spi3.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <SpiManager.h>
|
||||||
|
|
||||||
|
SemaphoreHandle_t paramLock = NULL;
|
||||||
|
#define SPI_PARAM_LOCK() \
|
||||||
|
do { \
|
||||||
|
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
||||||
|
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
||||||
|
|
||||||
|
static void IRAM_ATTR pre_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR post_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t spi;
|
||||||
|
gpio_num_t cs_reg, cs_fifo;
|
||||||
|
|
||||||
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed)
|
||||||
|
{
|
||||||
|
paramLock = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
auto bus_config = std::make_shared<SpiBusConfig>(
|
||||||
|
static_cast<gpio_num_t>(pin_sdio),
|
||||||
|
GPIO_NUM_NC,
|
||||||
|
static_cast<gpio_num_t>(pin_clk)
|
||||||
|
);
|
||||||
|
|
||||||
|
spi_device_interface_config_t device_config {
|
||||||
|
.command_bits = 0, // set by transactions individually
|
||||||
|
.address_bits = 0, // set by transactions individually
|
||||||
|
.dummy_bits = 0,
|
||||||
|
.mode = 0, // SPI mode 0
|
||||||
|
.duty_cycle_pos = 0,
|
||||||
|
.cs_ena_pretrans = 2, // only 1 pre and post cycle would be required for register access
|
||||||
|
.cs_ena_posttrans = static_cast<uint8_t>(2 * spi_speed / 1000000), // >2 us
|
||||||
|
.clock_speed_hz = spi_speed,
|
||||||
|
.input_delay_ns = 0,
|
||||||
|
.spics_io_num = -1, // CS handled by callbacks
|
||||||
|
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
||||||
|
.queue_size = 1,
|
||||||
|
.pre_cb = pre_cb,
|
||||||
|
.post_cb = post_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
spi = SpiManagerInst.alloc_device("", bus_config, device_config);
|
||||||
|
if (!spi)
|
||||||
|
ESP_ERROR_CHECK(ESP_FAIL);
|
||||||
|
|
||||||
|
cs_reg = static_cast<gpio_num_t>(pin_cs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_reg));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_reg, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_reg, GPIO_MODE_OUTPUT));
|
||||||
|
|
||||||
|
cs_fifo = static_cast<gpio_num_t>(pin_fcs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_fifo));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_fifo, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_fifo, GPIO_MODE_OUTPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write(const uint8_t addr, const uint8_t data)
|
||||||
|
{
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = &data,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t cmt_spi3_read(const uint8_t addr)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 1,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = &data,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.tx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.rx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
@ -3,7 +3,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed);
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed);
|
||||||
|
|
||||||
void cmt_spi3_write(const uint8_t addr, const uint8_t dat);
|
void cmt_spi3_write(const uint8_t addr, const uint8_t dat);
|
||||||
uint8_t cmt_spi3_read(const uint8_t addr);
|
uint8_t cmt_spi3_read(const uint8_t addr);
|
||||||
@ -11,4 +15,8 @@ uint8_t cmt_spi3_read(const uint8_t addr);
|
|||||||
void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len);
|
void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len);
|
||||||
void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len);
|
void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
57
lib/CpuTemperature/src/CpuTemperature.cpp
Normal file
57
lib/CpuTemperature/src/CpuTemperature.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CpuTemperature.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
|
// there is no official API available on the original ESP32
|
||||||
|
extern "C" {
|
||||||
|
uint8_t temprature_sens_read();
|
||||||
|
}
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
#include "driver/temp_sensor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CpuTemperatureClass CpuTemperature;
|
||||||
|
|
||||||
|
float CpuTemperatureClass::read()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
// Disabling temperature reading for ESP32-S2 models as it might lead to WDT resets.
|
||||||
|
// See: https://github.com/espressif/esp-idf/issues/8088
|
||||||
|
return NAN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
|
float temperature = NAN;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
|
uint8_t raw = temprature_sens_read();
|
||||||
|
ESP_LOGV(TAG, "Raw temperature value: %d", raw);
|
||||||
|
temperature = (raw - 32) / 1.8f;
|
||||||
|
success = (raw != 128);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT();
|
||||||
|
temp_sensor_set_config(tsens);
|
||||||
|
temp_sensor_start();
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3))
|
||||||
|
#error \
|
||||||
|
"ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271"
|
||||||
|
#endif
|
||||||
|
esp_err_t result = temp_sensor_read_celsius(&temperature);
|
||||||
|
temp_sensor_stop();
|
||||||
|
success = (result == ESP_OK);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (success && std::isfinite(temperature)) {
|
||||||
|
return temperature;
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Ignoring invalid temperature (success=%d, value=%.1f)", success, temperature);
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
lib/CpuTemperature/src/CpuTemperature.h
Normal file
14
lib/CpuTemperature/src/CpuTemperature.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class CpuTemperatureClass {
|
||||||
|
public:
|
||||||
|
float read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CpuTemperatureClass CpuTemperature;
|
||||||
@ -1,9 +1,12 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "inverters/HERF_1CH.h"
|
||||||
|
#include "inverters/HERF_2CH.h"
|
||||||
|
#include "inverters/HERF_4CH.h"
|
||||||
#include "inverters/HMS_1CH.h"
|
#include "inverters/HMS_1CH.h"
|
||||||
#include "inverters/HMS_1CHv2.h"
|
#include "inverters/HMS_1CHv2.h"
|
||||||
#include "inverters/HMS_2CH.h"
|
#include "inverters/HMS_2CH.h"
|
||||||
@ -54,7 +57,7 @@ void HoymilesClass::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iv != nullptr && iv->getRadio()->isInitialized() && iv->getRadio()->isQueueEmpty()) {
|
if (iv != nullptr && iv->getRadio()->isInitialized()) {
|
||||||
|
|
||||||
if (iv->getZeroValuesIfUnreachable() && !iv->isReachable()) {
|
if (iv->getZeroValuesIfUnreachable() && !iv->isReachable()) {
|
||||||
iv->Statistics()->zeroRuntimeData();
|
iv->Statistics()->zeroRuntimeData();
|
||||||
@ -112,10 +115,11 @@ void HoymilesClass::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch grid profile
|
// Fetch grid profile
|
||||||
if (iv->Statistics()->getLastUpdate() > 0 && iv->GridProfile()->getLastUpdate() == 0) {
|
if (iv->Statistics()->getLastUpdate() > 0 && (iv->GridProfile()->getLastUpdate() == 0 || !iv->GridProfile()->containsValidData())) {
|
||||||
iv->sendGridOnProFileParaRequest();
|
iv->sendGridOnProFileParaRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_messageOutput->printf("Queue size - NRF: %" PRId32 " CMT: %" PRId32 "\r\n", _radioNrf->getQueueSize(), _radioCmt->getQueueSize());
|
||||||
_lastPoll = millis();
|
_lastPoll = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +137,7 @@ void HoymilesClass::loop()
|
|||||||
if (currentWeekDay != lastWeekDay) {
|
if (currentWeekDay != lastWeekDay) {
|
||||||
|
|
||||||
for (auto& inv : _inverters) {
|
for (auto& inv : _inverters) {
|
||||||
// Have to reset the offets first, otherwise it will
|
inv->performDailyTask();
|
||||||
// Substract the offset from zero which leads to a high value
|
|
||||||
inv->Statistics()->resetYieldDayCorrection();
|
|
||||||
if (inv->getZeroYieldDayOnMidnight()) {
|
|
||||||
inv->Statistics()->zeroDailyData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastWeekDay = currentWeekDay;
|
lastWeekDay = currentWeekDay;
|
||||||
@ -168,6 +167,12 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, c
|
|||||||
i = std::make_shared<HM_2CH>(_radioNrf.get(), serial);
|
i = std::make_shared<HM_2CH>(_radioNrf.get(), serial);
|
||||||
} else if (HM_1CH::isValidSerial(serial)) {
|
} else if (HM_1CH::isValidSerial(serial)) {
|
||||||
i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
|
i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
|
||||||
|
} else if (HERF_1CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HERF_1CH>(_radioNrf.get(), serial);
|
||||||
|
} else if (HERF_2CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HERF_2CH>(_radioNrf.get(), serial);
|
||||||
|
} else if (HERF_4CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HERF_4CH>(_radioNrf.get(), serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i) {
|
if (i) {
|
||||||
@ -191,9 +196,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(const uint8_t
|
|||||||
|
|
||||||
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
for (auto& inv : _inverters) {
|
||||||
if (_inverters[i]->serial() == serial) {
|
if (inv->serial() == serial) {
|
||||||
return _inverters[i];
|
return inv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -205,9 +210,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(const fra
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InverterAbstract> inv;
|
for (auto& inv : _inverters) {
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
|
||||||
inv = _inverters[i];
|
|
||||||
serial_u p;
|
serial_u p;
|
||||||
p.u64 = inv->serial();
|
p.u64 = inv->serial();
|
||||||
|
|
||||||
@ -227,6 +230,7 @@ void HoymilesClass::removeInverterBySerial(const uint64_t serial)
|
|||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
||||||
if (_inverters[i]->serial() == serial) {
|
if (_inverters[i]->serial() == serial) {
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_inverters[i]->getRadio()->removeCommands(_inverters[i].get());
|
||||||
_inverters.erase(_inverters.begin() + i);
|
_inverters.erase(_inverters.begin() + i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -271,4 +275,4 @@ void HoymilesClass::setMessageOutput(Print* output)
|
|||||||
Print* HoymilesClass::getMessageOutput()
|
Print* HoymilesClass::getMessageOutput()
|
||||||
{
|
{
|
||||||
return _messageOutput;
|
return _messageOutput;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,16 +66,31 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
||||||
|
// Statistics: Count RX Fail No Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailNoAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
||||||
|
// Statistics: Count RX Fail Partial Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailPartialAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
||||||
Hoymiles.getMessageOutput()->println("Packet handling error");
|
Hoymiles.getMessageOutput()->println("Packet handling error");
|
||||||
|
// Statistics: Count RX Fail Corrupt Data
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailCorruptData++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
@ -83,17 +98,26 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
// Perform Retransmit
|
// Perform Retransmit
|
||||||
Hoymiles.getMessageOutput()->print("Request retransmit: ");
|
Hoymiles.getMessageOutput()->print("Request retransmit: ");
|
||||||
Hoymiles.getMessageOutput()->println(verifyResult);
|
Hoymiles.getMessageOutput()->println(verifyResult);
|
||||||
|
// Statistics: Count TX Re-Request Fragment
|
||||||
|
inv->RadioStats.TxReRequestFragment++;
|
||||||
|
|
||||||
sendRetransmitPacket(verifyResult);
|
sendRetransmitPacket(verifyResult);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Successful received all packages
|
// Successful received all packages
|
||||||
Hoymiles.getMessageOutput()->println("Success");
|
Hoymiles.getMessageOutput()->println("Success");
|
||||||
|
// Statistics: Count RX Success
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxSuccess++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If inverter was not found, assume the command is invalid
|
// If inverter was not found, assume the command is invalid
|
||||||
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
|
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
|
||||||
|
// Statistics: Count RX Fail Unknown Data
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
}
|
}
|
||||||
@ -105,6 +129,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
|
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
|
||||||
if (nullptr != inv) {
|
if (nullptr != inv) {
|
||||||
inv->clearRxFragmentBuffer();
|
inv->clearRxFragmentBuffer();
|
||||||
|
// Statistics: TX Requests
|
||||||
|
inv->RadioStats.TxRequestData++;
|
||||||
|
|
||||||
sendEsbPacket(*cmd);
|
sendEsbPacket(*cmd);
|
||||||
} else {
|
} else {
|
||||||
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
|
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
|
||||||
@ -129,6 +156,16 @@ bool HoymilesRadio::isInitialized() const
|
|||||||
return _isInitialized;
|
return _isInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::removeCommands(InverterAbstract* inv)
|
||||||
|
{
|
||||||
|
_commandQueue.removeAllEntriesForInverter(inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HoymilesRadio::countSimilarCommands(std::shared_ptr<CommandAbstract> cmd)
|
||||||
|
{
|
||||||
|
return _commandQueue.countSimilarCommands(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
bool HoymilesRadio::isIdle() const
|
bool HoymilesRadio::isIdle() const
|
||||||
{
|
{
|
||||||
return !_busyFlag;
|
return !_busyFlag;
|
||||||
@ -138,3 +175,8 @@ bool HoymilesRadio::isQueueEmpty() const
|
|||||||
{
|
{
|
||||||
return _commandQueue.size() == 0;
|
return _commandQueue.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t HoymilesRadio::getQueueSize() const
|
||||||
|
{
|
||||||
|
return _commandQueue.size();
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "TimeoutHelper.h"
|
#include "Arduino.h"
|
||||||
#include "commands/CommandAbstract.h"
|
#include "commands/CommandAbstract.h"
|
||||||
|
#include "queue/CommandQueue.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <memory>
|
#include <TimeoutHelper.h>
|
||||||
#include <ThreadSafeQueue.h>
|
|
||||||
|
#ifdef HOY_DEBUG_QUEUE
|
||||||
|
#define DEBUG_PRINT(fmt, args...) Serial.printf(fmt, ##args)
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINT(fmt, args...) /* Don't do anything in release builds */
|
||||||
|
#endif
|
||||||
|
|
||||||
class HoymilesRadio {
|
class HoymilesRadio {
|
||||||
public:
|
public:
|
||||||
@ -14,17 +20,54 @@ public:
|
|||||||
|
|
||||||
bool isIdle() const;
|
bool isIdle() const;
|
||||||
bool isQueueEmpty() const;
|
bool isQueueEmpty() const;
|
||||||
|
uint32_t getQueueSize() const;
|
||||||
bool isInitialized() const;
|
bool isInitialized() const;
|
||||||
|
|
||||||
|
void removeCommands(InverterAbstract* inv);
|
||||||
|
uint8_t countSimilarCommands(std::shared_ptr<CommandAbstract> cmd);
|
||||||
|
|
||||||
void enqueCommand(std::shared_ptr<CommandAbstract> cmd)
|
void enqueCommand(std::shared_ptr<CommandAbstract> cmd)
|
||||||
{
|
{
|
||||||
|
DEBUG_PRINT("Queue size before: %ld\r\n", _commandQueue.size());
|
||||||
|
DEBUG_PRINT("Handling command %s with type %d\r\n", cmd.get()->getCommandName().c_str(), static_cast<uint8_t>(cmd.get()->getQueueInsertType()));
|
||||||
|
switch (cmd.get()->getQueueInsertType()) {
|
||||||
|
case QueueInsertType::RemoveOldest:
|
||||||
|
_commandQueue.removeDuplicatedEntries(cmd);
|
||||||
|
break;
|
||||||
|
case QueueInsertType::ReplaceExistent:
|
||||||
|
// Checks if the queue already contains a command like the new one
|
||||||
|
// and replaces the existing one with the new one.
|
||||||
|
// (The new one will not be pushed at the end of the queue)
|
||||||
|
if (_commandQueue.countSimilarCommands(cmd) > 0) {
|
||||||
|
DEBUG_PRINT(" ... existing entry will be replaced\r\n");
|
||||||
|
_commandQueue.replaceEntries(cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QueueInsertType::RemoveNewest:
|
||||||
|
// Checks if the queue already contains a command like the new one
|
||||||
|
// and drops the new one. The new one will not be inserted.
|
||||||
|
if (_commandQueue.countSimilarCommands(cmd) > 0) {
|
||||||
|
DEBUG_PRINT(" ... new entry will be dropped\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QueueInsertType::AllowMultiple:
|
||||||
|
// Dont do anything, just fall through and insert the command.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the command into the queue if we reach this position of the code
|
||||||
|
DEBUG_PRINT(" ... new entry will be appended\r\n");
|
||||||
_commandQueue.push(cmd);
|
_commandQueue.push(cmd);
|
||||||
|
|
||||||
|
DEBUG_PRINT("Queue size after: %ld\r\n", _commandQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::shared_ptr<T> prepareCommand()
|
std::shared_ptr<T> prepareCommand(InverterAbstract* inv)
|
||||||
{
|
{
|
||||||
return std::make_shared<T>();
|
return std::make_shared<T>(inv);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -38,9 +81,9 @@ protected:
|
|||||||
void handleReceivedPackage();
|
void handleReceivedPackage();
|
||||||
|
|
||||||
serial_u _dtuSerial;
|
serial_u _dtuSerial;
|
||||||
ThreadSafeQueue<std::shared_ptr<CommandAbstract>> _commandQueue;
|
CommandQueue _commandQueue;
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
bool _busyFlag = false;
|
bool _busyFlag = false;
|
||||||
|
|
||||||
TimeoutHelper _rxTimeout;
|
TimeoutHelper _rxTimeout;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -34,7 +34,7 @@ uint32_t HoymilesRadio_CMT::getFrequencyFromChannel(const uint8_t channel) const
|
|||||||
uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) const
|
uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) const
|
||||||
{
|
{
|
||||||
if ((frequency % getChannelWidth()) != 0) {
|
if ((frequency % getChannelWidth()) != 0) {
|
||||||
Hoymiles.getMessageOutput()->printf("%.3f MHz is not divisible by %d kHz!\r\n", frequency / 1000000.0, getChannelWidth());
|
Hoymiles.getMessageOutput()->printf("%.3f MHz is not divisible by %" PRId32 " kHz!\r\n", frequency / 1000000.0, getChannelWidth());
|
||||||
return 0xFF; // ERROR
|
return 0xFF; // ERROR
|
||||||
}
|
}
|
||||||
if (frequency < getMinFrequency() || frequency > getMaxFrequency()) {
|
if (frequency < getMinFrequency() || frequency > getMaxFrequency()) {
|
||||||
@ -43,7 +43,7 @@ uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) con
|
|||||||
return 0xFF; // ERROR
|
return 0xFF; // ERROR
|
||||||
}
|
}
|
||||||
if (frequency < countryDefinition.at(_countryMode).Freq_Legal_Min || frequency > countryDefinition.at(_countryMode).Freq_Legal_Max) {
|
if (frequency < countryDefinition.at(_countryMode).Freq_Legal_Min || frequency > countryDefinition.at(_countryMode).Freq_Legal_Max) {
|
||||||
Hoymiles.getMessageOutput()->printf("!!! caution: %.2f MHz is out of region legal range! (%d - %d MHz)\r\n",
|
Hoymiles.getMessageOutput()->printf("!!! caution: %.2f MHz is out of region legal range! (%" PRId32 " - %" PRId32 " MHz)\r\n",
|
||||||
frequency / 1000000.0,
|
frequency / 1000000.0,
|
||||||
static_cast<uint32_t>(countryDefinition.at(_countryMode).Freq_Legal_Min / 1e6),
|
static_cast<uint32_t>(countryDefinition.at(_countryMode).Freq_Legal_Min / 1e6),
|
||||||
static_cast<uint32_t>(countryDefinition.at(_countryMode).Freq_Legal_Max / 1e6));
|
static_cast<uint32_t>(countryDefinition.at(_countryMode).Freq_Legal_Max / 1e6));
|
||||||
@ -167,9 +167,9 @@ void HoymilesRadio_CMT::loop()
|
|||||||
// Save packet in inverter rx buffer
|
// Save packet in inverter rx buffer
|
||||||
Hoymiles.getMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel) / 1000000.0);
|
Hoymiles.getMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel) / 1000000.0);
|
||||||
dumpBuf(f.fragment, f.len, false);
|
dumpBuf(f.fragment, f.len, false);
|
||||||
Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi);
|
Hoymiles.getMessageOutput()->printf("| %" PRId8 " dBm\r\n", f.rssi);
|
||||||
|
|
||||||
inv->addRxFragment(f.fragment, f.len);
|
inv->addRxFragment(f.fragment, f.len, f.rssi);
|
||||||
} else {
|
} else {
|
||||||
Hoymiles.getMessageOutput()->println("Inverter Not found!");
|
Hoymiles.getMessageOutput()->println("Inverter Not found!");
|
||||||
}
|
}
|
||||||
@ -194,9 +194,9 @@ void HoymilesRadio_CMT::setPALevel(const int8_t paLevel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_radio->setPALevel(paLevel)) {
|
if (_radio->setPALevel(paLevel)) {
|
||||||
Hoymiles.getMessageOutput()->printf("CMT TX power set to %d dBm\r\n", paLevel);
|
Hoymiles.getMessageOutput()->printf("CMT TX power set to %" PRId8 " dBm\r\n", paLevel);
|
||||||
} else {
|
} else {
|
||||||
Hoymiles.getMessageOutput()->printf("CMT TX power %d dBm is not defined! (min: -10 dBm, max: 20 dBm)\r\n", paLevel);
|
Hoymiles.getMessageOutput()->printf("CMT TX power %" PRId8 " dBm is not defined! (min: -10 dBm, max: 20 dBm)\r\n", paLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,8 +239,11 @@ CountryModeId_t HoymilesRadio_CMT::getCountryMode() const
|
|||||||
|
|
||||||
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
|
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
|
||||||
{
|
{
|
||||||
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
|
|
||||||
_countryMode = mode;
|
_countryMode = mode;
|
||||||
|
if (!_isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const
|
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const
|
||||||
|
|||||||
@ -76,11 +76,11 @@ void HoymilesRadio_NRF::loop()
|
|||||||
|
|
||||||
if (nullptr != inv) {
|
if (nullptr != inv) {
|
||||||
// Save packet in inverter rx buffer
|
// Save packet in inverter rx buffer
|
||||||
Hoymiles.getMessageOutput()->printf("RX Channel: %d --> ", f.channel);
|
Hoymiles.getMessageOutput()->printf("RX Channel: %" PRId8 " --> ", f.channel);
|
||||||
dumpBuf(f.fragment, f.len, false);
|
dumpBuf(f.fragment, f.len, false);
|
||||||
Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi);
|
Hoymiles.getMessageOutput()->printf("| %" PRId8 " dBm\r\n", f.rssi);
|
||||||
|
|
||||||
inv->addRxFragment(f.fragment, f.len);
|
inv->addRxFragment(f.fragment, f.len, f.rssi);
|
||||||
} else {
|
} else {
|
||||||
Hoymiles.getMessageOutput()->println("Inverter Not found!");
|
Hoymiles.getMessageOutput()->println("Inverter Not found!");
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract& cmd)
|
|||||||
openWritingPipe(s);
|
openWritingPipe(s);
|
||||||
_radio->setRetries(3, 15);
|
_radio->setRetries(3, 15);
|
||||||
|
|
||||||
Hoymiles.getMessageOutput()->printf("TX %s Channel: %d --> ",
|
Hoymiles.getMessageOutput()->printf("TX %s Channel: %" PRId8 " --> ",
|
||||||
cmd.getCommandName().c_str(), _radio->getChannel());
|
cmd.getCommandName().c_str(), _radio->getChannel());
|
||||||
cmd.dumpDataPayload(Hoymiles.getMessageOutput());
|
cmd.dumpDataPayload(Hoymiles.getMessageOutput());
|
||||||
_radio->write(cmd.getDataPayload(), cmd.getDataSize());
|
_radio->write(cmd.getDataPayload(), cmd.getDataSize());
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -25,8 +25,8 @@ ID Target Addr Source Addr Cmd SCmd ? Limit Type CRC16 CRC8
|
|||||||
|
|
||||||
#define CRC_SIZE 6
|
#define CRC_SIZE 6
|
||||||
|
|
||||||
ActivePowerControlCommand::ActivePowerControlCommand(const uint64_t target_address, const uint64_t router_address)
|
ActivePowerControlCommand::ActivePowerControlCommand(InverterAbstract* inv, const uint64_t router_address)
|
||||||
: DevControlCommand(target_address, router_address)
|
: DevControlCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[10] = 0x0b;
|
_payload[10] = 0x0b;
|
||||||
_payload[11] = 0x00;
|
_payload[11] = 0x00;
|
||||||
@ -44,7 +44,15 @@ ActivePowerControlCommand::ActivePowerControlCommand(const uint64_t target_addre
|
|||||||
|
|
||||||
String ActivePowerControlCommand::getCommandName() const
|
String ActivePowerControlCommand::getCommandName() const
|
||||||
{
|
{
|
||||||
return "ActivePowerControl";
|
char buffer[30];
|
||||||
|
snprintf(buffer, sizeof(buffer), "ActivePowerControl (%02X)", getType());
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActivePowerControlCommand::areSameParameter(CommandAbstract* other)
|
||||||
|
{
|
||||||
|
return CommandAbstract::areSameParameter(other)
|
||||||
|
&& this->getType() == static_cast<ActivePowerControlCommand*>(other)->getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivePowerControlCommand::setActivePowerLimit(const float limit, const PowerLimitControlType type)
|
void ActivePowerControlCommand::setActivePowerLimit(const float limit, const PowerLimitControlType type)
|
||||||
@ -62,39 +70,42 @@ void ActivePowerControlCommand::setActivePowerLimit(const float limit, const Pow
|
|||||||
udpateCRC(CRC_SIZE);
|
udpateCRC(CRC_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ActivePowerControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool ActivePowerControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!DevControlCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((getType() == PowerLimitControlType::RelativNonPersistent) || (getType() == PowerLimitControlType::RelativPersistent)) {
|
if ((getType() == PowerLimitControlType::RelativNonPersistent) || (getType() == PowerLimitControlType::RelativPersistent)) {
|
||||||
inverter.SystemConfigPara()->setLimitPercent(getLimit());
|
_inv->SystemConfigPara()->setLimitPercent(getLimit());
|
||||||
} else {
|
} else {
|
||||||
const uint16_t max_power = inverter.DevInfo()->getMaxPower();
|
const uint16_t max_power = _inv->DevInfo()->getMaxPower();
|
||||||
if (max_power > 0) {
|
if (max_power > 0) {
|
||||||
inverter.SystemConfigPara()->setLimitPercent(static_cast<float>(getLimit()) / max_power * 100);
|
_inv->SystemConfigPara()->setLimitPercent(static_cast<float>(getLimit()) / max_power * 100);
|
||||||
} else {
|
} else {
|
||||||
// TODO(tbnobody): Not implemented yet because we only can publish the percentage value
|
// TODO(tbnobody): Not implemented yet because we only can publish the percentage value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inverter.SystemConfigPara()->setLastUpdateCommand(millis());
|
_inv->SystemConfigPara()->setLastUpdateCommand(millis());
|
||||||
inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK);
|
std::shared_ptr<ActivePowerControlCommand> cmd(std::shared_ptr<ActivePowerControlCommand>(), this);
|
||||||
|
if (_inv->getRadio()->countSimilarCommands(cmd) == 1) {
|
||||||
|
_inv->SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ActivePowerControlCommand::getLimit() const
|
float ActivePowerControlCommand::getLimit() const
|
||||||
{
|
{
|
||||||
const uint16_t l = (((uint16_t)_payload[12] << 8) | _payload[13]);
|
const float l = (static_cast<uint16_t>(_payload[12]) << 8) | _payload[13];
|
||||||
return l / 10;
|
return l / 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerLimitControlType ActivePowerControlCommand::getType()
|
PowerLimitControlType ActivePowerControlCommand::getType() const
|
||||||
{
|
{
|
||||||
return (PowerLimitControlType)(((uint16_t)_payload[14] << 8) | _payload[15]);
|
return (PowerLimitControlType)((static_cast<uint16_t>(_payload[14]) << 8) | _payload[15]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivePowerControlCommand::gotTimeout(InverterAbstract& inverter)
|
void ActivePowerControlCommand::gotTimeout()
|
||||||
{
|
{
|
||||||
inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK);
|
_inv->SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,14 +12,16 @@ typedef enum { // ToDo: to be verified by field tests
|
|||||||
|
|
||||||
class ActivePowerControlCommand : public DevControlCommand {
|
class ActivePowerControlCommand : public DevControlCommand {
|
||||||
public:
|
public:
|
||||||
explicit ActivePowerControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit ActivePowerControlCommand(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
virtual QueueInsertType getQueueInsertType() const { return QueueInsertType::RemoveOldest; }
|
||||||
|
virtual bool areSameParameter(CommandAbstract* other);
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
virtual void gotTimeout(InverterAbstract& inverter);
|
virtual void gotTimeout();
|
||||||
|
|
||||||
void setActivePowerLimit(const float limit, const PowerLimitControlType type = RelativNonPersistent);
|
void setActivePowerLimit(const float limit, const PowerLimitControlType type = RelativNonPersistent);
|
||||||
float getLimit() const;
|
float getLimit() const;
|
||||||
PowerLimitControlType getType();
|
PowerLimitControlType getType() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -23,8 +23,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap AlarmId Pa
|
|||||||
#include "AlarmDataCommand.h"
|
#include "AlarmDataCommand.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
AlarmDataCommand::AlarmDataCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
AlarmDataCommand::AlarmDataCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x11);
|
setDataType(0x11);
|
||||||
@ -36,28 +36,28 @@ String AlarmDataCommand::getCommandName() const
|
|||||||
return "AlarmData";
|
return "AlarmData";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlarmDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool AlarmDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.EventLog()->beginAppendFragment();
|
_inv->EventLog()->beginAppendFragment();
|
||||||
inverter.EventLog()->clearBuffer();
|
_inv->EventLog()->clearBuffer();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
_inv->EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.EventLog()->endAppendFragment();
|
_inv->EventLog()->endAppendFragment();
|
||||||
inverter.EventLog()->setLastAlarmRequestSuccess(CMD_OK);
|
_inv->EventLog()->setLastAlarmRequestSuccess(CMD_OK);
|
||||||
inverter.EventLog()->setLastUpdate(millis());
|
_inv->EventLog()->setLastUpdate(millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmDataCommand::gotTimeout(InverterAbstract& inverter)
|
void AlarmDataCommand::gotTimeout()
|
||||||
{
|
{
|
||||||
inverter.EventLog()->setLastAlarmRequestSuccess(CMD_NOK);
|
_inv->EventLog()->setLastAlarmRequestSuccess(CMD_NOK);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
class AlarmDataCommand : public MultiDataCommand {
|
class AlarmDataCommand : public MultiDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit AlarmDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
|
explicit AlarmDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
virtual void gotTimeout(InverterAbstract& inverter);
|
virtual void gotTimeout();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,8 +19,8 @@ ID Target Addr Source Addr ? ? ? CH ? CRC8
|
|||||||
*/
|
*/
|
||||||
#include "ChannelChangeCommand.h"
|
#include "ChannelChangeCommand.h"
|
||||||
|
|
||||||
ChannelChangeCommand::ChannelChangeCommand(const uint64_t target_address, const uint64_t router_address, const uint8_t channel)
|
ChannelChangeCommand::ChannelChangeCommand(InverterAbstract* inv, const uint64_t router_address, const uint8_t channel)
|
||||||
: CommandAbstract(target_address, router_address)
|
: CommandAbstract(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[0] = 0x56;
|
_payload[0] = 0x56;
|
||||||
_payload[13] = 0x14;
|
_payload[13] = 0x14;
|
||||||
@ -67,7 +67,7 @@ void ChannelChangeCommand::setCountryMode(const CountryModeId_t mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChannelChangeCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool ChannelChangeCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
class ChannelChangeCommand : public CommandAbstract {
|
class ChannelChangeCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit ChannelChangeCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const uint8_t channel = 0);
|
explicit ChannelChangeCommand(InverterAbstract* inv, const uint64_t router_address = 0, const uint8_t channel = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ public:
|
|||||||
|
|
||||||
void setCountryMode(const CountryModeId_t mode);
|
void setCountryMode(const CountryModeId_t mode);
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
|
|
||||||
virtual uint8_t getMaxResendCount();
|
virtual uint8_t getMaxResendCount();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -29,13 +29,16 @@ Source Address: 80 12 23 04
|
|||||||
#include "CommandAbstract.h"
|
#include "CommandAbstract.h"
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "../inverters/InverterAbstract.h"
|
||||||
|
|
||||||
CommandAbstract::CommandAbstract(const uint64_t target_address, const uint64_t router_address)
|
CommandAbstract::CommandAbstract(InverterAbstract* inv, const uint64_t router_address)
|
||||||
{
|
{
|
||||||
memset(_payload, 0, RF_LEN);
|
memset(_payload, 0, RF_LEN);
|
||||||
_payload_size = 0;
|
_payload_size = 0;
|
||||||
|
|
||||||
setTargetAddress(target_address);
|
_inv = inv;
|
||||||
|
|
||||||
|
setTargetAddress(_inv->serial());
|
||||||
setRouterAddress(router_address);
|
setRouterAddress(router_address);
|
||||||
setSendCount(0);
|
setSendCount(0);
|
||||||
setTimeout(0);
|
setTimeout(0);
|
||||||
@ -122,7 +125,7 @@ void CommandAbstract::convertSerialToPacketId(uint8_t buffer[], const uint64_t s
|
|||||||
buffer[0] = s.b[3];
|
buffer[0] = s.b[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandAbstract::gotTimeout(InverterAbstract& inverter)
|
void CommandAbstract::gotTimeout()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,3 +138,9 @@ uint8_t CommandAbstract::getMaxRetransmitCount() const
|
|||||||
{
|
{
|
||||||
return MAX_RETRANSMIT_COUNT;
|
return MAX_RETRANSMIT_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommandAbstract::areSameParameter(CommandAbstract* other)
|
||||||
|
{
|
||||||
|
return this->getCommandName() == other->getCommandName()
|
||||||
|
&& this->_targetAddress == other->getTargetAddress();
|
||||||
|
}
|
||||||
|
|||||||
@ -11,9 +11,21 @@
|
|||||||
|
|
||||||
class InverterAbstract;
|
class InverterAbstract;
|
||||||
|
|
||||||
|
enum class QueueInsertType {
|
||||||
|
AllowMultiple,
|
||||||
|
// Remove from beginning of the queue
|
||||||
|
RemoveOldest,
|
||||||
|
|
||||||
|
// Don't insert command if it already exist
|
||||||
|
RemoveNewest,
|
||||||
|
|
||||||
|
// Replace the existing entry in the queue by the one to be added
|
||||||
|
ReplaceExistent,
|
||||||
|
};
|
||||||
|
|
||||||
class CommandAbstract {
|
class CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit CommandAbstract(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit CommandAbstract(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
virtual ~CommandAbstract() {};
|
virtual ~CommandAbstract() {};
|
||||||
|
|
||||||
const uint8_t* getDataPayload();
|
const uint8_t* getDataPayload();
|
||||||
@ -21,7 +33,6 @@ public:
|
|||||||
|
|
||||||
uint8_t getDataSize() const;
|
uint8_t getDataSize() const;
|
||||||
|
|
||||||
void setTargetAddress(const uint64_t address);
|
|
||||||
uint64_t getTargetAddress() const;
|
uint64_t getTargetAddress() const;
|
||||||
|
|
||||||
void setRouterAddress(const uint64_t address);
|
void setRouterAddress(const uint64_t address);
|
||||||
@ -38,8 +49,8 @@ public:
|
|||||||
|
|
||||||
virtual CommandAbstract* getRequestFrameCommand(const uint8_t frame_no);
|
virtual CommandAbstract* getRequestFrameCommand(const uint8_t frame_no);
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) = 0;
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) = 0;
|
||||||
virtual void gotTimeout(InverterAbstract& inverter);
|
virtual void gotTimeout();
|
||||||
|
|
||||||
// Sets the amount how often the specific command is resent if all fragments where missing
|
// Sets the amount how often the specific command is resent if all fragments where missing
|
||||||
virtual uint8_t getMaxResendCount() const;
|
virtual uint8_t getMaxResendCount() const;
|
||||||
@ -47,6 +58,10 @@ public:
|
|||||||
// Sets the amount how often a missing fragment is re-requested if it was not available
|
// Sets the amount how often a missing fragment is re-requested if it was not available
|
||||||
virtual uint8_t getMaxRetransmitCount() const;
|
virtual uint8_t getMaxRetransmitCount() const;
|
||||||
|
|
||||||
|
// Returns whether multiple instances of this command are allowed in the command queue.
|
||||||
|
virtual QueueInsertType getQueueInsertType() const { return QueueInsertType::RemoveNewest; }
|
||||||
|
virtual bool areSameParameter(CommandAbstract* other);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t _payload[RF_LEN];
|
uint8_t _payload[RF_LEN];
|
||||||
uint8_t _payload_size;
|
uint8_t _payload_size;
|
||||||
@ -56,6 +71,9 @@ protected:
|
|||||||
uint64_t _targetAddress;
|
uint64_t _targetAddress;
|
||||||
uint64_t _routerAddress;
|
uint64_t _routerAddress;
|
||||||
|
|
||||||
|
InverterAbstract* _inv;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setTargetAddress(const uint64_t address);
|
||||||
static void convertSerialToPacketId(uint8_t buffer[], const uint64_t serial);
|
static void convertSerialToPacketId(uint8_t buffer[], const uint64_t serial);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -23,8 +23,8 @@ ID Target Addr Source Addr Cmd Payload CRC16 CRC8
|
|||||||
#include "DevControlCommand.h"
|
#include "DevControlCommand.h"
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
|
|
||||||
DevControlCommand::DevControlCommand(const uint64_t target_address, const uint64_t router_address)
|
DevControlCommand::DevControlCommand(InverterAbstract* inv, const uint64_t router_address)
|
||||||
: CommandAbstract(target_address, router_address)
|
: CommandAbstract(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[0] = 0x51;
|
_payload[0] = 0x51;
|
||||||
_payload[9] = 0x81;
|
_payload[9] = 0x81;
|
||||||
@ -35,11 +35,11 @@ DevControlCommand::DevControlCommand(const uint64_t target_address, const uint64
|
|||||||
void DevControlCommand::udpateCRC(const uint8_t len)
|
void DevControlCommand::udpateCRC(const uint8_t len)
|
||||||
{
|
{
|
||||||
const uint16_t crc = crc16(&_payload[10], len);
|
const uint16_t crc = crc16(&_payload[10], len);
|
||||||
_payload[10 + len] = (uint8_t)(crc >> 8);
|
_payload[10 + len] = static_cast<uint8_t>(crc >> 8);
|
||||||
_payload[10 + len + 1] = (uint8_t)(crc);
|
_payload[10 + len + 1] = static_cast<uint8_t>(crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool DevControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
if (fragment[i].mainCmd != (_payload[0] | 0x80)) {
|
if (fragment[i].mainCmd != (_payload[0] | 0x80)) {
|
||||||
@ -48,4 +48,4 @@ bool DevControlCommand::handleResponse(InverterAbstract& inverter, const fragmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
class DevControlCommand : public CommandAbstract {
|
class DevControlCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit DevControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit DevControlCommand(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void udpateCRC(const uint8_t len);
|
void udpateCRC(const uint8_t len);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -21,8 +21,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "DevInfoAllCommand.h"
|
#include "DevInfoAllCommand.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
DevInfoAllCommand::DevInfoAllCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
DevInfoAllCommand::DevInfoAllCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x01);
|
setDataType(0x01);
|
||||||
@ -34,22 +34,22 @@ String DevInfoAllCommand::getCommandName() const
|
|||||||
return "DevInfoAll";
|
return "DevInfoAll";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevInfoAllCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool DevInfoAllCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.DevInfo()->beginAppendFragment();
|
_inv->DevInfo()->beginAppendFragment();
|
||||||
inverter.DevInfo()->clearBufferAll();
|
_inv->DevInfo()->clearBufferAll();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.DevInfo()->appendFragmentAll(offs, fragment[i].fragment, fragment[i].len);
|
_inv->DevInfo()->appendFragmentAll(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.DevInfo()->endAppendFragment();
|
_inv->DevInfo()->endAppendFragment();
|
||||||
inverter.DevInfo()->setLastUpdateAll(millis());
|
_inv->DevInfo()->setLastUpdateAll(millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
class DevInfoAllCommand : public MultiDataCommand {
|
class DevInfoAllCommand : public MultiDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit DevInfoAllCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
|
explicit DevInfoAllCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -21,8 +21,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "DevInfoSimpleCommand.h"
|
#include "DevInfoSimpleCommand.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
DevInfoSimpleCommand::DevInfoSimpleCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
DevInfoSimpleCommand::DevInfoSimpleCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x00);
|
setDataType(0x00);
|
||||||
@ -34,22 +34,22 @@ String DevInfoSimpleCommand::getCommandName() const
|
|||||||
return "DevInfoSimple";
|
return "DevInfoSimple";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevInfoSimpleCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool DevInfoSimpleCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.DevInfo()->beginAppendFragment();
|
_inv->DevInfo()->beginAppendFragment();
|
||||||
inverter.DevInfo()->clearBufferSimple();
|
_inv->DevInfo()->clearBufferSimple();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len);
|
_inv->DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.DevInfo()->endAppendFragment();
|
_inv->DevInfo()->endAppendFragment();
|
||||||
inverter.DevInfo()->setLastUpdateSimple(millis());
|
_inv->DevInfo()->setLastUpdateSimple(millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
class DevInfoSimpleCommand : public MultiDataCommand {
|
class DevInfoSimpleCommand : public MultiDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit DevInfoSimpleCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
|
explicit DevInfoSimpleCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
GridOnProFilePara::GridOnProFilePara(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
GridOnProFilePara::GridOnProFilePara(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x02);
|
setDataType(0x02);
|
||||||
@ -35,22 +35,22 @@ String GridOnProFilePara::getCommandName() const
|
|||||||
return "GridOnProFilePara";
|
return "GridOnProFilePara";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GridOnProFilePara::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool GridOnProFilePara::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.GridProfile()->beginAppendFragment();
|
_inv->GridProfile()->beginAppendFragment();
|
||||||
inverter.GridProfile()->clearBuffer();
|
_inv->GridProfile()->clearBuffer();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.GridProfile()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
_inv->GridProfile()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.GridProfile()->endAppendFragment();
|
_inv->GridProfile()->endAppendFragment();
|
||||||
inverter.GridProfile()->setLastUpdate(millis());
|
_inv->GridProfile()->setLastUpdate(millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
class GridOnProFilePara : public MultiDataCommand {
|
class GridOnProFilePara : public MultiDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit GridOnProFilePara(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
|
explicit GridOnProFilePara(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -28,8 +28,9 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "MultiDataCommand.h"
|
#include "MultiDataCommand.h"
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
|
|
||||||
MultiDataCommand::MultiDataCommand(const uint64_t target_address, const uint64_t router_address, const uint8_t data_type, const time_t time)
|
MultiDataCommand::MultiDataCommand(InverterAbstract* inv, const uint64_t router_address, const uint8_t data_type, const time_t time)
|
||||||
: CommandAbstract(target_address, router_address)
|
: CommandAbstract(inv, router_address)
|
||||||
|
, _cmdRequestFrame(inv)
|
||||||
{
|
{
|
||||||
_payload[0] = 0x15;
|
_payload[0] = 0x15;
|
||||||
_payload[9] = 0x80;
|
_payload[9] = 0x80;
|
||||||
@ -62,10 +63,10 @@ uint8_t MultiDataCommand::getDataType() const
|
|||||||
|
|
||||||
void MultiDataCommand::setTime(const time_t time)
|
void MultiDataCommand::setTime(const time_t time)
|
||||||
{
|
{
|
||||||
_payload[12] = (uint8_t)(time >> 24);
|
_payload[12] = static_cast<uint8_t>(time >> 24);
|
||||||
_payload[13] = (uint8_t)(time >> 16);
|
_payload[13] = static_cast<uint8_t>(time >> 16);
|
||||||
_payload[14] = (uint8_t)(time >> 8);
|
_payload[14] = static_cast<uint8_t>(time >> 8);
|
||||||
_payload[15] = (uint8_t)(time);
|
_payload[15] = static_cast<uint8_t>(time);
|
||||||
udpateCRC();
|
udpateCRC();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,13 +80,12 @@ time_t MultiDataCommand::getTime() const
|
|||||||
|
|
||||||
CommandAbstract* MultiDataCommand::getRequestFrameCommand(const uint8_t frame_no)
|
CommandAbstract* MultiDataCommand::getRequestFrameCommand(const uint8_t frame_no)
|
||||||
{
|
{
|
||||||
_cmdRequestFrame.setTargetAddress(getTargetAddress());
|
|
||||||
_cmdRequestFrame.setFrameNo(frame_no);
|
_cmdRequestFrame.setFrameNo(frame_no);
|
||||||
|
|
||||||
return &_cmdRequestFrame;
|
return &_cmdRequestFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MultiDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool MultiDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// All fragments are available --> Check CRC
|
// All fragments are available --> Check CRC
|
||||||
uint16_t crc = 0xffff, crcRcv = 0;
|
uint16_t crc = 0xffff, crcRcv = 0;
|
||||||
@ -112,8 +112,8 @@ bool MultiDataCommand::handleResponse(InverterAbstract& inverter, const fragment
|
|||||||
void MultiDataCommand::udpateCRC()
|
void MultiDataCommand::udpateCRC()
|
||||||
{
|
{
|
||||||
const uint16_t crc = crc16(&_payload[10], 14); // From data_type till password
|
const uint16_t crc = crc16(&_payload[10], 14); // From data_type till password
|
||||||
_payload[24] = (uint8_t)(crc >> 8);
|
_payload[24] = static_cast<uint8_t>(crc >> 8);
|
||||||
_payload[25] = (uint8_t)(crc);
|
_payload[25] = static_cast<uint8_t>(crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MultiDataCommand::getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id)
|
uint8_t MultiDataCommand::getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
|
|||||||
@ -7,14 +7,14 @@
|
|||||||
|
|
||||||
class MultiDataCommand : public CommandAbstract {
|
class MultiDataCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit MultiDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const uint8_t data_type = 0, const time_t time = 0);
|
explicit MultiDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const uint8_t data_type = 0, const time_t time = 0);
|
||||||
|
|
||||||
void setTime(const time_t time);
|
void setTime(const time_t time);
|
||||||
time_t getTime() const;
|
time_t getTime() const;
|
||||||
|
|
||||||
CommandAbstract* getRequestFrameCommand(const uint8_t frame_no);
|
CommandAbstract* getRequestFrameCommand(const uint8_t frame_no);
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setDataType(const uint8_t data_type);
|
void setDataType(const uint8_t data_type);
|
||||||
@ -23,4 +23,4 @@ protected:
|
|||||||
static uint8_t getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id);
|
static uint8_t getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
|
|
||||||
RequestFrameCommand _cmdRequestFrame;
|
RequestFrameCommand _cmdRequestFrame;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
#include "ParaSetCommand.h"
|
#include "ParaSetCommand.h"
|
||||||
|
|
||||||
ParaSetCommand::ParaSetCommand(const uint64_t target_address, const uint64_t router_address)
|
ParaSetCommand::ParaSetCommand(InverterAbstract* inv, const uint64_t router_address)
|
||||||
: CommandAbstract(target_address, router_address)
|
: CommandAbstract(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[0] = 0x52;
|
_payload[0] = 0x52;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,5 +5,5 @@
|
|||||||
|
|
||||||
class ParaSetCommand : public CommandAbstract {
|
class ParaSetCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit ParaSetCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit ParaSetCommand(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -26,8 +26,8 @@ ID Target Addr Source Addr Cmd SCmd ? CRC16 CRC8
|
|||||||
|
|
||||||
#define CRC_SIZE 2
|
#define CRC_SIZE 2
|
||||||
|
|
||||||
PowerControlCommand::PowerControlCommand(const uint64_t target_address, const uint64_t router_address)
|
PowerControlCommand::PowerControlCommand(InverterAbstract* inv, const uint64_t router_address)
|
||||||
: DevControlCommand(target_address, router_address)
|
: DevControlCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[10] = 0x00; // TurnOn
|
_payload[10] = 0x00; // TurnOn
|
||||||
_payload[11] = 0x00;
|
_payload[11] = 0x00;
|
||||||
@ -44,20 +44,20 @@ String PowerControlCommand::getCommandName() const
|
|||||||
return "PowerControl";
|
return "PowerControl";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PowerControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool PowerControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!DevControlCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inverter.PowerCommand()->setLastUpdateCommand(millis());
|
_inv->PowerCommand()->setLastUpdateCommand(millis());
|
||||||
inverter.PowerCommand()->setLastPowerCommandSuccess(CMD_OK);
|
_inv->PowerCommand()->setLastPowerCommandSuccess(CMD_OK);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerControlCommand::gotTimeout(InverterAbstract& inverter)
|
void PowerControlCommand::gotTimeout()
|
||||||
{
|
{
|
||||||
inverter.PowerCommand()->setLastPowerCommandSuccess(CMD_NOK);
|
_inv->PowerCommand()->setLastPowerCommandSuccess(CMD_NOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerControlCommand::setPowerOn(const bool state)
|
void PowerControlCommand::setPowerOn(const bool state)
|
||||||
@ -76,4 +76,4 @@ void PowerControlCommand::setRestart()
|
|||||||
_payload[10] = 0x02; // Restart
|
_payload[10] = 0x02; // Restart
|
||||||
|
|
||||||
udpateCRC(CRC_SIZE); // 2 byte crc
|
udpateCRC(CRC_SIZE); // 2 byte crc
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,14 @@
|
|||||||
|
|
||||||
class PowerControlCommand : public DevControlCommand {
|
class PowerControlCommand : public DevControlCommand {
|
||||||
public:
|
public:
|
||||||
explicit PowerControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit PowerControlCommand(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
virtual QueueInsertType getQueueInsertType() const { return QueueInsertType::AllowMultiple; }
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
virtual void gotTimeout(InverterAbstract& inverter);
|
virtual void gotTimeout();
|
||||||
|
|
||||||
void setPowerOn(const bool state);
|
void setPowerOn(const bool state);
|
||||||
void setRestart();
|
void setRestart();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
RealTimeRunDataCommand::RealTimeRunDataCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
RealTimeRunDataCommand::RealTimeRunDataCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x0b);
|
setDataType(0x0b);
|
||||||
@ -35,10 +35,10 @@ String RealTimeRunDataCommand::getCommandName() const
|
|||||||
return "RealTimeRunData";
|
return "RealTimeRunData";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool RealTimeRunDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,9 +46,9 @@ bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fr
|
|||||||
// In case of low power in the inverter it occours that some incomplete fragments
|
// In case of low power in the inverter it occours that some incomplete fragments
|
||||||
// with a valid CRC are received.
|
// with a valid CRC are received.
|
||||||
const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id);
|
const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id);
|
||||||
const uint8_t expectedSize = inverter.Statistics()->getExpectedByteCount();
|
const uint8_t expectedSize = _inv->Statistics()->getExpectedByteCount();
|
||||||
if (fragmentsSize < expectedSize) {
|
if (fragmentsSize < expectedSize) {
|
||||||
Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %d, min expected size: %d\r\n",
|
Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %" PRId8 ", min expected size: %" PRId8 "\r\n",
|
||||||
getCommandName().c_str(), fragmentsSize, expectedSize);
|
getCommandName().c_str(), fragmentsSize, expectedSize);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -56,19 +56,19 @@ bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fr
|
|||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.Statistics()->beginAppendFragment();
|
_inv->Statistics()->beginAppendFragment();
|
||||||
inverter.Statistics()->clearBuffer();
|
_inv->Statistics()->clearBuffer();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.Statistics()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
_inv->Statistics()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.Statistics()->endAppendFragment();
|
_inv->Statistics()->endAppendFragment();
|
||||||
inverter.Statistics()->resetRxFailureCount();
|
_inv->Statistics()->resetRxFailureCount();
|
||||||
inverter.Statistics()->setLastUpdate(millis());
|
_inv->Statistics()->setLastUpdate(millis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RealTimeRunDataCommand::gotTimeout(InverterAbstract& inverter)
|
void RealTimeRunDataCommand::gotTimeout()
|
||||||
{
|
{
|
||||||
inverter.Statistics()->incrementRxFailureCount();
|
_inv->Statistics()->incrementRxFailureCount();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
class RealTimeRunDataCommand : public MultiDataCommand {
|
class RealTimeRunDataCommand : public MultiDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit RealTimeRunDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0);
|
explicit RealTimeRunDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
virtual void gotTimeout(InverterAbstract& inverter);
|
virtual void gotTimeout();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -22,8 +22,8 @@ ID Target Addr Source Addr Frm CRC8
|
|||||||
*/
|
*/
|
||||||
#include "RequestFrameCommand.h"
|
#include "RequestFrameCommand.h"
|
||||||
|
|
||||||
RequestFrameCommand::RequestFrameCommand(const uint64_t target_address, const uint64_t router_address, uint8_t frame_no)
|
RequestFrameCommand::RequestFrameCommand(InverterAbstract* inv, const uint64_t router_address, uint8_t frame_no)
|
||||||
: SingleDataCommand(target_address, router_address)
|
: SingleDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
if (frame_no > 127) {
|
if (frame_no > 127) {
|
||||||
frame_no = 0;
|
frame_no = 0;
|
||||||
@ -47,7 +47,7 @@ uint8_t RequestFrameCommand::getFrameNo() const
|
|||||||
return _payload[9] & (~0x80);
|
return _payload[9] & (~0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestFrameCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool RequestFrameCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
class RequestFrameCommand : public SingleDataCommand {
|
class RequestFrameCommand : public SingleDataCommand {
|
||||||
public:
|
public:
|
||||||
explicit RequestFrameCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, uint8_t frame_no = 0);
|
explicit RequestFrameCommand(InverterAbstract* inv, const uint64_t router_address = 0, uint8_t frame_no = 0);
|
||||||
|
|
||||||
virtual String getCommandName() const;
|
virtual String getCommandName() const;
|
||||||
|
|
||||||
void setFrameNo(const uint8_t frame_no);
|
void setFrameNo(const uint8_t frame_no);
|
||||||
uint8_t getFrameNo() const;
|
uint8_t getFrameNo() const;
|
||||||
|
|
||||||
virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id);
|
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -19,8 +19,8 @@ ID Target Addr Source Addr CRC8
|
|||||||
*/
|
*/
|
||||||
#include "SingleDataCommand.h"
|
#include "SingleDataCommand.h"
|
||||||
|
|
||||||
SingleDataCommand::SingleDataCommand(const uint64_t target_address, const uint64_t router_address)
|
SingleDataCommand::SingleDataCommand(InverterAbstract* inv, const uint64_t router_address)
|
||||||
: CommandAbstract(target_address, router_address)
|
: CommandAbstract(inv, router_address)
|
||||||
{
|
{
|
||||||
_payload[0] = 0x15;
|
_payload[0] = 0x15;
|
||||||
setTimeout(100);
|
setTimeout(100);
|
||||||
|
|||||||
@ -5,5 +5,5 @@
|
|||||||
|
|
||||||
class SingleDataCommand : public CommandAbstract {
|
class SingleDataCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
explicit SingleDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0);
|
explicit SingleDataCommand(InverterAbstract* inv, const uint64_t router_address = 0);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2022-2023 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa
|
|||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
#include "inverters/InverterAbstract.h"
|
#include "inverters/InverterAbstract.h"
|
||||||
|
|
||||||
SystemConfigParaCommand::SystemConfigParaCommand(const uint64_t target_address, const uint64_t router_address, const time_t time)
|
SystemConfigParaCommand::SystemConfigParaCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time)
|
||||||
: MultiDataCommand(target_address, router_address)
|
: MultiDataCommand(inv, router_address)
|
||||||
{
|
{
|
||||||
setTime(time);
|
setTime(time);
|
||||||
setDataType(0x05);
|
setDataType(0x05);
|
||||||
@ -35,10 +35,10 @@ String SystemConfigParaCommand::getCommandName() const
|
|||||||
return "SystemConfigPara";
|
return "SystemConfigPara";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id)
|
bool SystemConfigParaCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id)
|
||||||
{
|
{
|
||||||
// Check CRC of whole payload
|
// Check CRC of whole payload
|
||||||
if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) {
|
if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,9 +46,9 @@ bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const f
|
|||||||
// In case of low power in the inverter it occours that some incomplete fragments
|
// In case of low power in the inverter it occours that some incomplete fragments
|
||||||
// with a valid CRC are received.
|
// with a valid CRC are received.
|
||||||
const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id);
|
const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id);
|
||||||
const uint8_t expectedSize = inverter.SystemConfigPara()->getExpectedByteCount();
|
const uint8_t expectedSize = _inv->SystemConfigPara()->getExpectedByteCount();
|
||||||
if (fragmentsSize < expectedSize) {
|
if (fragmentsSize < expectedSize) {
|
||||||
Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %d, min expected size: %d\r\n",
|
Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %" PRId8 ", min expected size: %" PRId8 "\r\n",
|
||||||
getCommandName().c_str(), fragmentsSize, expectedSize);
|
getCommandName().c_str(), fragmentsSize, expectedSize);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -56,19 +56,19 @@ bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const f
|
|||||||
|
|
||||||
// Move all fragments into target buffer
|
// Move all fragments into target buffer
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
inverter.SystemConfigPara()->beginAppendFragment();
|
_inv->SystemConfigPara()->beginAppendFragment();
|
||||||
inverter.SystemConfigPara()->clearBuffer();
|
_inv->SystemConfigPara()->clearBuffer();
|
||||||
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
for (uint8_t i = 0; i < max_fragment_id; i++) {
|
||||||
inverter.SystemConfigPara()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
_inv->SystemConfigPara()->appendFragment(offs, fragment[i].fragment, fragment[i].len);
|
||||||
offs += (fragment[i].len);
|
offs += (fragment[i].len);
|
||||||
}
|
}
|
||||||
inverter.SystemConfigPara()->endAppendFragment();
|
_inv->SystemConfigPara()->endAppendFragment();
|
||||||
inverter.SystemConfigPara()->setLastUpdateRequest(millis());
|
_inv->SystemConfigPara()->setLastUpdateRequest(millis());
|
||||||
inverter.SystemConfigPara()->setLastLimitRequestSuccess(CMD_OK);
|
_inv->SystemConfigPara()->setLastLimitRequestSuccess(CMD_OK);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemConfigParaCommand::gotTimeout(InverterAbstract& inverter)
|
void SystemConfigParaCommand::gotTimeout()
|
||||||
{
|
{
|
||||||
inverter.SystemConfigPara()->setLastLimitRequestSuccess(CMD_NOK);
|
_inv->SystemConfigPara()->setLastLimitRequestSuccess(CMD_NOK);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user