2
.github/workflows/python-app.yml
vendored
2
.github/workflows/python-app.yml
vendored
@@ -5,7 +5,7 @@ name: Python application
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "dev-*" ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md' # Do no build on *.md changes
|
- '**.md' # Do no build on *.md changes
|
||||||
- '**.yml' # Do no build on *.yml changes
|
- '**.yml' # Do no build on *.yml changes
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Prepare support of inverters with 6 MTPPs
|
||||||
|
- Clear `Daily Generation` values at midnigth
|
||||||
|
- Read pv module details from config file and use it for the Home Assistant registration
|
||||||
|
see: [#43](https://github.com/s-allius/tsun-gen3-proxy/issues/43)
|
||||||
|
- migrate to aiomqtt version 2.0.0
|
||||||
|
|
||||||
## [0.6.0] - 2024-04-02
|
## [0.6.0] - 2024-04-02
|
||||||
|
|
||||||
- Refactoring to support Solarman V5 protocol
|
- Refactoring to support Solarman V5 protocol
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -7,7 +7,8 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://opensource.org/licenses/BSD-3-Clause"><img alt="License: BSD-3-Clause" src="https://img.shields.io/badge/License-BSD_3--Clause-green.svg"></a>
|
<a href="https://opensource.org/licenses/BSD-3-Clause"><img alt="License: BSD-3-Clause" src="https://img.shields.io/badge/License-BSD_3--Clause-green.svg"></a>
|
||||||
<a href="https://www.python.org/downloads/release/python-3120/"><img alt="Supported Python versions" src="https://img.shields.io/badge/python-3.12-blue.svg"></a>
|
<a href="https://www.python.org/downloads/release/python-3120/"><img alt="Supported Python versions" src="https://img.shields.io/badge/python-3.12-blue.svg"></a>
|
||||||
<a href="https://sbtinstruments.github.io/aiomqtt/introduction.html"><img alt="Supported aiomqtt versions" src="https://img.shields.io/badge/aiomqtt-1.2.1-lightblue.svg"></a>
|
<a href="https://sbtinstruments.github.io/aiomqtt/introduction.html"><img alt="Supported aiomqtt versions" src="https://img.shields.io/badge/aiomqtt-2.0.0-lightblue.svg"></a>
|
||||||
|
<a href="https://libraries.io/pypi/aiocron"><img alt="Supported aiocron versions" src="https://img.shields.io/badge/aiocron-1.8-lightblue.svg"></a>
|
||||||
<a href="https://toml.io/en/v1.0.0"><img alt="Supported toml versions" src="https://img.shields.io/badge/toml-1.0.0-lightblue.svg"></a>
|
<a href="https://toml.io/en/v1.0.0"><img alt="Supported toml versions" src="https://img.shields.io/badge/toml-1.0.0-lightblue.svg"></a>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
@@ -43,7 +44,7 @@ If you use a Pi-hole, you can also store the host entry in the Pi-hole.
|
|||||||
- supports TSUN GEN3 inverters: TSOL-MS800, MS700, MS600, MS400, MS350 and MS300
|
- supports TSUN GEN3 inverters: TSOL-MS800, MS700, MS600, MS400, MS350 and MS300
|
||||||
- `MQTT` support
|
- `MQTT` support
|
||||||
- `Home-Assistant` auto-discovery support
|
- `Home-Assistant` auto-discovery support
|
||||||
- Self-sufficient island operation without internet (for TSUN GEN3 PLUS inverters in preparation)
|
- Self-sufficient island operation without internet
|
||||||
- runs in a non-root Docker Container
|
- runs in a non-root Docker Container
|
||||||
|
|
||||||
## Home Assistant Screenshots
|
## Home Assistant Screenshots
|
||||||
@@ -127,15 +128,23 @@ inverters.allow_all = false # True: allow inverters, even if we have no invert
|
|||||||
[inverters."R17xxxxxxxxxxxx1"]
|
[inverters."R17xxxxxxxxxxxx1"]
|
||||||
node_id = 'inv1' # Optional, MQTT replacement for inverters serial number
|
node_id = 'inv1' # Optional, MQTT replacement for inverters serial number
|
||||||
suggested_area = 'roof' # Optional, suggested installation area for home-assistant
|
suggested_area = 'roof' # Optional, suggested installation area for home-assistant
|
||||||
|
pv1 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv2 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
[inverters."R17xxxxxxxxxxxx2"]
|
[inverters."R17xxxxxxxxxxxx2"]
|
||||||
node_id = 'inv2' # Optional, MQTT replacement for inverters serial number
|
node_id = 'inv2' # Optional, MQTT replacement for inverters serial number
|
||||||
suggested_area = 'balcony' # Optional, suggested installation area for home-assistant
|
suggested_area = 'balcony' # Optional, suggested installation area for home-assistant
|
||||||
|
pv1 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv2 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
[inverters."Y17xxxxxxxxxxxx1"]
|
[inverters."Y17xxxxxxxxxxxx1"]
|
||||||
monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter
|
monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter
|
||||||
node_id = 'inv_3' # MQTT replacement for inverters serial number
|
node_id = 'inv_3' # MQTT replacement for inverters serial number
|
||||||
suggested_area = 'garage' # suggested installation place for home-assistant
|
suggested_area = 'garage' # suggested installation place for home-assistant
|
||||||
|
pv1 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv2 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv3 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
pv4 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
BUILD_DATE=$(date -Iminutes)
|
BUILD_DATE=$(date -Iminutes)
|
||||||
|
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
VERSION=$(git describe --tags --abbrev=0)
|
VERSION=$(git describe --tags --abbrev=0)
|
||||||
VERSION="${VERSION:1}"
|
VERSION="${VERSION:1}"
|
||||||
arr=(${VERSION//./ })
|
arr=(${VERSION//./ })
|
||||||
@@ -19,7 +20,7 @@ IMAGE=tsun-gen3-proxy
|
|||||||
|
|
||||||
if [[ $1 == dev ]] || [[ $1 == rc ]] ;then
|
if [[ $1 == dev ]] || [[ $1 == rc ]] ;then
|
||||||
IMAGE=docker.io/sallius/${IMAGE}
|
IMAGE=docker.io/sallius/${IMAGE}
|
||||||
VERSION=${VERSION}-$1
|
VERSION=${VERSION}-$1-${BRANCH}
|
||||||
elif [[ $1 == rel ]];then
|
elif [[ $1 == rel ]];then
|
||||||
IMAGE=ghcr.io/s-allius/${IMAGE}
|
IMAGE=ghcr.io/s-allius/${IMAGE}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -31,13 +31,21 @@ inverters.allow_all = true # allow inverters, even if we have no inverter mapp
|
|||||||
[inverters."R170000000000001"]
|
[inverters."R170000000000001"]
|
||||||
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
||||||
#suggested_area = '' # Optional, suggested installation area for home-assistant
|
#suggested_area = '' # Optional, suggested installation area for home-assistant
|
||||||
|
#pv1 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
#pv2 = {type = 'RSM40-8-395M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
#[inverters."R17xxxxxxxxxxxx2"]
|
#[inverters."R17xxxxxxxxxxxx2"]
|
||||||
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
||||||
#suggested_area = '' # Optional, suggested installation area for home-assistant
|
#suggested_area = '' # Optional, suggested installation area for home-assistant
|
||||||
|
#pv1 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
#pv2 = {type = 'RSM40-8-405M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
[inverters."Y170000000000001"]
|
[inverters."Y170000000000001"]
|
||||||
#monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter
|
monitor_sn = 2000000000 # The "Monitoring SN:" can be found on a sticker enclosed with the inverter
|
||||||
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
#node_id = '' # Optional, MQTT replacement for inverters serial number
|
||||||
#suggested_area = '' # Optional, suggested installation place for home-assistant
|
#suggested_area = '' # Optional, suggested installation place for home-assistant
|
||||||
|
#pv1 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
#pv2 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
#pv3 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
#pv4 = {type = 'RSM40-8-410M', manufacturer = 'Risen'} # Optional, PV module descr
|
||||||
|
|
||||||
|
|||||||
379
app/proxy.svg
379
app/proxy.svg
@@ -4,275 +4,340 @@
|
|||||||
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
|
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
|
||||||
-->
|
-->
|
||||||
<!-- Title: G Pages: 1 -->
|
<!-- Title: G Pages: 1 -->
|
||||||
<svg width="520pt" height="1060pt"
|
<svg width="511pt" height="1204pt"
|
||||||
viewBox="0.00 0.00 519.50 1060.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
viewBox="0.00 0.00 511.39 1204.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1056)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1200)">
|
||||||
<title>G</title>
|
<title>G</title>
|
||||||
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1056 515.5,-1056 515.5,4 -4,4"/>
|
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1200 507.3928,-1200 507.3928,4 -4,4"/>
|
||||||
<!-- A0 -->
|
<!-- A0 -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>A0</title>
|
<title>A0</title>
|
||||||
<polygon fill="#fff8dc" stroke="#000000" points="113.6964,-1028 5.3036,-1028 5.3036,-992 119.6964,-992 119.6964,-1022 113.6964,-1028"/>
|
<polygon fill="#fff8dc" stroke="#000000" points="148.1964,-1100 39.8036,-1100 39.8036,-1064 154.1964,-1064 154.1964,-1094 148.1964,-1100"/>
|
||||||
<polyline fill="none" stroke="#000000" points="113.6964,-1028 113.6964,-1022 "/>
|
<polyline fill="none" stroke="#000000" points="148.1964,-1100 148.1964,-1094 "/>
|
||||||
<polyline fill="none" stroke="#000000" points="119.6964,-1022 113.6964,-1022 "/>
|
<polyline fill="none" stroke="#000000" points="154.1964,-1094 148.1964,-1094 "/>
|
||||||
<text text-anchor="middle" x="62.5" y="-1013" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">You can stick notes</text>
|
<text text-anchor="middle" x="97" y="-1085" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">You can stick notes</text>
|
||||||
<text text-anchor="middle" x="62.5" y="-1001" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">on diagrams too!</text>
|
<text text-anchor="middle" x="97" y="-1073" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">on diagrams too!</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A1 -->
|
<!-- A1 -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>A1</title>
|
<title>A1</title>
|
||||||
<polygon fill="none" stroke="#000000" points="485.1817,-804 415.8183,-804 415.8183,-768 485.1817,-768 485.1817,-804"/>
|
<polygon fill="none" stroke="#000000" points="95.6817,-804 26.3183,-804 26.3183,-768 95.6817,-768 95.6817,-804"/>
|
||||||
<text text-anchor="middle" x="450.5" y="-783" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Singleton</text>
|
<text text-anchor="middle" x="61" y="-783" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Singleton</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A2 -->
|
<!-- A2 -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>A2</title>
|
<title>A2</title>
|
||||||
<polygon fill="none" stroke="#000000" points="389.5,-518 389.5,-550 511.5,-550 511.5,-518 389.5,-518"/>
|
<polygon fill="none" stroke="#000000" points="0,-518 0,-550 122,-550 122,-518 0,-518"/>
|
||||||
<text text-anchor="start" x="440.777" y="-531" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Mqtt</text>
|
<text text-anchor="start" x="51.277" y="-531" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Mqtt</text>
|
||||||
<polygon fill="none" stroke="#000000" points="389.5,-462 389.5,-518 511.5,-518 511.5,-462 389.5,-462"/>
|
<polygon fill="none" stroke="#000000" points="0,-462 0,-518 122,-518 122,-462 0,-462"/>
|
||||||
<text text-anchor="start" x="407.9875" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>ha_restarts</text>
|
<text text-anchor="start" x="18.4875" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>ha_restarts</text>
|
||||||
<text text-anchor="start" x="415.7665" y="-487" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>__client</text>
|
<text text-anchor="start" x="26.2665" y="-487" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>__client</text>
|
||||||
<text text-anchor="start" x="399.3735" y="-475" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>__cb_MqttIsUp</text>
|
<text text-anchor="start" x="9.8735" y="-475" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><static>__cb_MqttIsUp</text>
|
||||||
<polygon fill="none" stroke="#000000" points="389.5,-418 389.5,-462 511.5,-462 511.5,-418 389.5,-418"/>
|
<polygon fill="none" stroke="#000000" points="0,-418 0,-462 122,-462 122,-418 0,-418"/>
|
||||||
<text text-anchor="start" x="412.436" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>publish()</text>
|
<text text-anchor="start" x="22.936" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>publish()</text>
|
||||||
<text text-anchor="start" x="416.6045" y="-431" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>close()</text>
|
<text text-anchor="start" x="27.1045" y="-431" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A1->A2 -->
|
<!-- A1->A2 -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>A1->A2</title>
|
<title>A1->A2</title>
|
||||||
<path fill="none" stroke="#000000" d="M450.5,-757.4632C450.5,-710.3291 450.5,-615.0013 450.5,-550.3153"/>
|
<path fill="none" stroke="#000000" d="M61,-757.4632C61,-710.3291 61,-615.0013 61,-550.3153"/>
|
||||||
<polygon fill="none" stroke="#000000" points="447.0001,-757.5631 450.5,-767.5632 454.0001,-757.5632 447.0001,-757.5631"/>
|
<polygon fill="none" stroke="#000000" points="57.5001,-757.5631 61,-767.5632 64.5001,-757.5632 57.5001,-757.5631"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A10 -->
|
<!-- A10 -->
|
||||||
<g id="node11" class="node">
|
<g id="node11" class="node">
|
||||||
<title>A10</title>
|
<title>A10</title>
|
||||||
<polygon fill="none" stroke="#000000" points="396.5,-282 396.5,-314 504.5,-314 504.5,-282 396.5,-282"/>
|
<polygon fill="none" stroke="#000000" points="7,-282 7,-314 115,-314 115,-282 7,-282"/>
|
||||||
<text text-anchor="start" x="433.5535" y="-295" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Inverter</text>
|
<text text-anchor="start" x="44.0535" y="-295" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Inverter</text>
|
||||||
<polygon fill="none" stroke="#000000" points="396.5,-190 396.5,-282 504.5,-282 504.5,-190 396.5,-190"/>
|
<polygon fill="none" stroke="#000000" points="7,-190 7,-282 115,-282 115,-190 7,-190"/>
|
||||||
<text text-anchor="start" x="426.604" y="-263" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.db_stat</text>
|
<text text-anchor="start" x="37.104" y="-263" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.db_stat</text>
|
||||||
<text text-anchor="start" x="419.9405" y="-251" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.entity_prfx</text>
|
<text text-anchor="start" x="30.4405" y="-251" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.entity_prfx</text>
|
||||||
<text text-anchor="start" x="410.7755" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.discovery_prfx</text>
|
<text text-anchor="start" x="21.2755" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.discovery_prfx</text>
|
||||||
<text text-anchor="start" x="410.2115" y="-227" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.proxy_node_id</text>
|
<text text-anchor="start" x="20.7115" y="-227" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.proxy_node_id</text>
|
||||||
<text text-anchor="start" x="406.3225" y="-215" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.proxy_unique_id</text>
|
<text text-anchor="start" x="16.8225" y="-215" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.proxy_unique_id</text>
|
||||||
<text text-anchor="start" x="422.1655" y="-203" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.mqtt:Mqtt</text>
|
<text text-anchor="start" x="32.6655" y="-203" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">cls.mqtt:Mqtt</text>
|
||||||
<polygon fill="none" stroke="#000000" points="396.5,-170 396.5,-190 504.5,-190 504.5,-170 396.5,-170"/>
|
<polygon fill="none" stroke="#000000" points="7,-170 7,-190 115,-190 115,-170 7,-170"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A2->A10 -->
|
<!-- A2->A10 -->
|
||||||
<g id="edge11" class="edge">
|
<g id="edge11" class="edge">
|
||||||
<title>A2->A10</title>
|
<title>A2->A10</title>
|
||||||
<path fill="none" stroke="#000000" d="M450.5,-417.8724C450.5,-385.8251 450.5,-347.2624 450.5,-314.4235"/>
|
<path fill="none" stroke="#000000" d="M61,-417.8724C61,-385.8251 61,-347.2624 61,-314.4235"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A3 -->
|
<!-- A3 -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>A3</title>
|
<title>A3</title>
|
||||||
<polygon fill="none" stroke="#000000" points="138.5,-1020 138.5,-1052 209.5,-1052 209.5,-1020 138.5,-1020"/>
|
<polygon fill="none" stroke="#000000" points="173,-1092 173,-1124 244,-1124 244,-1092 173,-1092"/>
|
||||||
<text text-anchor="start" x="148.445" y="-1033" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">IterRegistry</text>
|
<text text-anchor="start" x="182.945" y="-1105" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">IterRegistry</text>
|
||||||
<polygon fill="none" stroke="#000000" points="138.5,-1000 138.5,-1020 209.5,-1020 209.5,-1000 138.5,-1000"/>
|
<polygon fill="none" stroke="#000000" points="173,-1072 173,-1092 244,-1092 244,-1072 173,-1072"/>
|
||||||
<polygon fill="none" stroke="#000000" points="138.5,-968 138.5,-1000 209.5,-1000 209.5,-968 138.5,-968"/>
|
<polygon fill="none" stroke="#000000" points="173,-1040 173,-1072 244,-1072 244,-1040 173,-1040"/>
|
||||||
<text text-anchor="start" x="155.939" y="-981" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__iter__</text>
|
<text text-anchor="start" x="190.439" y="-1053" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__iter__</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A4 -->
|
<!-- A4 -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>A4</title>
|
<title>A4</title>
|
||||||
<polygon fill="none" stroke="#000000" points="106.5,-886 106.5,-918 240.5,-918 240.5,-886 106.5,-886"/>
|
<polygon fill="none" stroke="#000000" points="141,-886 141,-918 275,-918 275,-886 141,-886"/>
|
||||||
<text text-anchor="start" x="153.2175" y="-899" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Message</text>
|
<text text-anchor="start" x="187.7175" y="-899" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Message</text>
|
||||||
<polygon fill="none" stroke="#000000" points="106.5,-722 106.5,-886 240.5,-886 240.5,-722 106.5,-722"/>
|
<polygon fill="none" stroke="#000000" points="141,-722 141,-886 275,-886 275,-722 141,-722"/>
|
||||||
<text text-anchor="start" x="136.8265" y="-867" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">server_side:bool</text>
|
<text text-anchor="start" x="171.3265" y="-867" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">server_side:bool</text>
|
||||||
<text text-anchor="start" x="134.043" y="-855" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">header_valid:bool</text>
|
<text text-anchor="start" x="168.543" y="-855" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">header_valid:bool</text>
|
||||||
<text text-anchor="start" x="126.814" y="-843" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">header_len:unsigned</text>
|
<text text-anchor="start" x="161.314" y="-843" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">header_len:unsigned</text>
|
||||||
<text text-anchor="start" x="132.648" y="-831" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">data_len:unsigned</text>
|
<text text-anchor="start" x="167.148" y="-831" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">data_len:unsigned</text>
|
||||||
<text text-anchor="start" x="151.8245" y="-819" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">unique_id</text>
|
<text text-anchor="start" x="186.3245" y="-819" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">unique_id</text>
|
||||||
<text text-anchor="start" x="155.7135" y="-807" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">node_id</text>
|
<text text-anchor="start" x="190.2135" y="-807" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">node_id</text>
|
||||||
<text text-anchor="start" x="152.6585" y="-795" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">sug_area</text>
|
<text text-anchor="start" x="187.1585" y="-795" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">sug_area</text>
|
||||||
<text text-anchor="start" x="123.489" y="-783" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_recv_buffer:bytearray</text>
|
<text text-anchor="start" x="157.989" y="-783" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_recv_buffer:bytearray</text>
|
||||||
<text text-anchor="start" x="122.0945" y="-771" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_send_buffer:bytearray</text>
|
<text text-anchor="start" x="156.5945" y="-771" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_send_buffer:bytearray</text>
|
||||||
<text text-anchor="start" x="116.2665" y="-759" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_forward_buffer:bytearray</text>
|
<text text-anchor="start" x="150.7665" y="-759" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_forward_buffer:bytearray</text>
|
||||||
<text text-anchor="start" x="155.7135" y="-747" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">db:Infos</text>
|
<text text-anchor="start" x="190.2135" y="-747" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">db:Infos</text>
|
||||||
<text text-anchor="start" x="144.326" y="-735" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">new_data:list</text>
|
<text text-anchor="start" x="178.826" y="-735" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">new_data:list</text>
|
||||||
<polygon fill="none" stroke="#000000" points="106.5,-654 106.5,-722 240.5,-722 240.5,-654 106.5,-654"/>
|
<polygon fill="none" stroke="#000000" points="141,-654 141,-722 275,-722 275,-654 141,-654"/>
|
||||||
<text text-anchor="start" x="123.2095" y="-703" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_read():void<abstract></text>
|
<text text-anchor="start" x="157.7095" y="-703" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">_read():void<abstract></text>
|
||||||
<text text-anchor="start" x="147.9445" y="-691" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close():void</text>
|
<text text-anchor="start" x="182.4445" y="-691" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close():void</text>
|
||||||
<text text-anchor="start" x="133.7725" y="-679" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">inc_counter():void</text>
|
<text text-anchor="start" x="168.2725" y="-679" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">inc_counter():void</text>
|
||||||
<text text-anchor="start" x="132.1025" y="-667" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">dec_counter():void</text>
|
<text text-anchor="start" x="166.6025" y="-667" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">dec_counter():void</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A3->A4 -->
|
<!-- A3->A4 -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>A3->A4</title>
|
<title>A3->A4</title>
|
||||||
<path fill="none" stroke="#000000" d="M173.5,-957.5789C173.5,-945.4616 173.5,-932.0319 173.5,-918.1761"/>
|
<path fill="none" stroke="#000000" d="M208,-1029.7414C208,-998.6043 208,-957.5621 208,-918.0536"/>
|
||||||
<polygon fill="none" stroke="#000000" points="170.0001,-957.8673 173.5,-967.8673 177.0001,-957.8673 170.0001,-957.8673"/>
|
<polygon fill="none" stroke="#000000" points="204.5001,-1029.9047 208,-1039.9048 211.5001,-1029.9048 204.5001,-1029.9047"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A5 -->
|
<!-- A5 -->
|
||||||
<g id="node6" class="node">
|
<g id="node6" class="node">
|
||||||
<title>A5</title>
|
<title>A5</title>
|
||||||
<polygon fill="none" stroke="#000000" points="243.5,-566 243.5,-598 357.5,-598 357.5,-566 243.5,-566"/>
|
<polygon fill="none" stroke="#000000" points="145,-566 145,-598 259,-598 259,-566 145,-566"/>
|
||||||
<text text-anchor="start" x="286.608" y="-579" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Talent</text>
|
<text text-anchor="start" x="188.108" y="-579" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Talent</text>
|
||||||
<polygon fill="none" stroke="#000000" points="243.5,-486 243.5,-566 357.5,-566 357.5,-486 243.5,-486"/>
|
<polygon fill="none" stroke="#000000" points="145,-486 145,-566 259,-566 259,-486 145,-486"/>
|
||||||
<text text-anchor="start" x="253.263" y="-547" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">await_conn_resp_cnt</text>
|
<text text-anchor="start" x="154.763" y="-547" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">await_conn_resp_cnt</text>
|
||||||
<text text-anchor="start" x="288.2775" y="-535" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">id_str</text>
|
<text text-anchor="start" x="189.7775" y="-535" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">id_str</text>
|
||||||
<text text-anchor="start" x="269.1" y="-523" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">contact_name</text>
|
<text text-anchor="start" x="170.6" y="-523" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">contact_name</text>
|
||||||
<text text-anchor="start" x="272.44" y="-511" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">contact_mail</text>
|
<text text-anchor="start" x="173.94" y="-511" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">contact_mail</text>
|
||||||
<text text-anchor="start" x="286.612" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">switch</text>
|
<text text-anchor="start" x="188.112" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">switch</text>
|
||||||
<polygon fill="none" stroke="#000000" points="243.5,-370 243.5,-486 357.5,-486 357.5,-370 243.5,-370"/>
|
<polygon fill="none" stroke="#000000" points="145,-370 145,-486 259,-486 259,-370 145,-370"/>
|
||||||
<text text-anchor="start" x="257.9925" y="-467" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_contact_info()</text>
|
<text text-anchor="start" x="159.4925" y="-467" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_contact_info()</text>
|
||||||
<text text-anchor="start" x="259.9325" y="-455" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_ota_update()</text>
|
<text text-anchor="start" x="161.4325" y="-455" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_ota_update()</text>
|
||||||
<text text-anchor="start" x="265.7765" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_get_time()</text>
|
<text text-anchor="start" x="167.2765" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_get_time()</text>
|
||||||
<text text-anchor="start" x="253.8285" y="-431" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_collector_data()</text>
|
<text text-anchor="start" x="155.3285" y="-431" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_collector_data()</text>
|
||||||
<text text-anchor="start" x="255.7735" y="-419" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_inverter_data()</text>
|
<text text-anchor="start" x="157.2735" y="-419" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_inverter_data()</text>
|
||||||
<text text-anchor="start" x="264.9405" y="-407" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_unknown()</text>
|
<text text-anchor="start" x="166.4405" y="-407" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_unknown()</text>
|
||||||
<text text-anchor="start" x="285.5025" y="-383" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="187.0025" y="-383" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A4->A5 -->
|
<!-- A4->A5 -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>A4->A5</title>
|
<title>A4->A5</title>
|
||||||
<path fill="none" stroke="#000000" d="M233.058,-644.3739C239.5598,-628.913 246.1169,-613.3205 252.4553,-598.2481"/>
|
<path fill="none" stroke="#000000" d="M205.1775,-643.9363C204.8732,-628.6188 204.5665,-613.1783 204.2698,-598.2481"/>
|
||||||
<polygon fill="none" stroke="#000000" points="229.6695,-643.4029 229.0193,-653.9777 236.1222,-646.1164 229.6695,-643.4029"/>
|
<polygon fill="none" stroke="#000000" points="201.679,-644.0493 205.377,-653.9777 208.6776,-643.9102 201.679,-644.0493"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A6 -->
|
<!-- A6 -->
|
||||||
<g id="node7" class="node">
|
<g id="node7" class="node">
|
||||||
<title>A6</title>
|
<title>A6</title>
|
||||||
<polygon fill="none" stroke="#000000" points=".5,-530 .5,-562 91.5,-562 91.5,-530 .5,-530"/>
|
<polygon fill="none" stroke="#000000" points="412,-530 412,-562 503,-562 503,-530 412,-530"/>
|
||||||
<text text-anchor="start" x="18.495" y="-543" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">SolarmanV5</text>
|
<text text-anchor="start" x="429.995" y="-543" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">SolarmanV5</text>
|
||||||
<polygon fill="none" stroke="#000000" points=".5,-462 .5,-530 91.5,-530 91.5,-462 .5,-462"/>
|
<polygon fill="none" stroke="#000000" points="412,-462 412,-530 503,-530 503,-462 412,-462"/>
|
||||||
<text text-anchor="start" x="30.998" y="-511" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">control</text>
|
<text text-anchor="start" x="442.498" y="-511" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">control</text>
|
||||||
<text text-anchor="start" x="34.0575" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">serial</text>
|
<text text-anchor="start" x="445.5575" y="-499" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">serial</text>
|
||||||
<text text-anchor="start" x="39.056" y="-487" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">snr</text>
|
<text text-anchor="start" x="450.556" y="-487" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">snr</text>
|
||||||
<text text-anchor="start" x="32.112" y="-475" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">switch</text>
|
<text text-anchor="start" x="443.612" y="-475" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">switch</text>
|
||||||
<polygon fill="none" stroke="#000000" points=".5,-406 .5,-462 91.5,-462 91.5,-406 .5,-406"/>
|
<polygon fill="none" stroke="#000000" points="412,-406 412,-462 503,-462 503,-406 412,-406"/>
|
||||||
<text text-anchor="start" x="10.4405" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_unknown()</text>
|
<text text-anchor="start" x="421.9405" y="-443" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">msg_unknown()</text>
|
||||||
<text text-anchor="start" x="31.0025" y="-419" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="442.5025" y="-419" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A4->A6 -->
|
<!-- A4->A6 -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>A4->A6</title>
|
<title>A4->A6</title>
|
||||||
<path fill="none" stroke="#000000" d="M113.4937,-644.4225C101.5234,-616.1802 89.3661,-587.4965 78.7055,-562.3442"/>
|
<path fill="none" stroke="#000000" d="M281.898,-681.5056C290.821,-671.6784 300.2479,-662.304 310,-654 345.4324,-623.8293 370.2318,-638.0075 402,-604 413.2639,-591.9422 422.5424,-577.1747 430.0614,-562.1755"/>
|
||||||
<polygon fill="none" stroke="#000000" points="110.4186,-646.1364 117.5435,-653.9777 116.8636,-643.4047 110.4186,-646.1364"/>
|
<polygon fill="none" stroke="#000000" points="279.184,-679.2912 275.1758,-689.0986 284.4251,-683.9313 279.184,-679.2912"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A7 -->
|
<!-- A7 -->
|
||||||
<g id="node8" class="node">
|
<g id="node8" class="node">
|
||||||
<title>A7</title>
|
<title>A7</title>
|
||||||
<polygon fill="none" stroke="#000000" points="210.5,-258 210.5,-290 360.5,-290 360.5,-258 210.5,-258"/>
|
<polygon fill="none" stroke="#000000" points="133,-258 133,-290 283,-290 283,-258 133,-258"/>
|
||||||
<text text-anchor="start" x="253.5455" y="-271" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3</text>
|
<text text-anchor="start" x="176.0455" y="-271" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3</text>
|
||||||
<polygon fill="none" stroke="#000000" points="210.5,-226 210.5,-258 360.5,-258 360.5,-226 210.5,-226"/>
|
<polygon fill="none" stroke="#000000" points="133,-226 133,-258 283,-258 283,-226 133,-226"/>
|
||||||
<text text-anchor="start" x="220.487" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3</text>
|
<text text-anchor="start" x="142.987" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3</text>
|
||||||
<polygon fill="none" stroke="#000000" points="210.5,-194 210.5,-226 360.5,-226 360.5,-194 210.5,-194"/>
|
<polygon fill="none" stroke="#000000" points="133,-194 133,-226 283,-226 283,-194 133,-194"/>
|
||||||
<text text-anchor="start" x="270.5025" y="-207" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="193.0025" y="-207" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A5->A7 -->
|
<!-- A5->A7 -->
|
||||||
<g id="edge5" class="edge">
|
<g id="edge5" class="edge">
|
||||||
<title>A5->A7</title>
|
<title>A5->A7</title>
|
||||||
<path fill="none" stroke="#000000" d="M292.7856,-359.5407C291.2573,-334.8843 289.7409,-310.4196 288.4905,-290.2462"/>
|
<path fill="none" stroke="#000000" d="M205.0858,-359.5407C205.6971,-334.8843 206.3036,-310.4196 206.8038,-290.2462"/>
|
||||||
<polygon fill="none" stroke="#000000" points="289.3053,-359.968 293.4173,-369.7323 296.2918,-359.5349 289.3053,-359.968"/>
|
<polygon fill="none" stroke="#000000" points="201.5821,-359.6485 204.8331,-369.7323 208.58,-359.8221 201.5821,-359.6485"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A8 -->
|
<!-- A8 -->
|
||||||
<g id="node9" class="node">
|
<g id="node9" class="node">
|
||||||
<title>A8</title>
|
<title>A8</title>
|
||||||
<polygon fill="none" stroke="#000000" points="18.5,-258 18.5,-290 174.5,-290 174.5,-258 18.5,-258"/>
|
<polygon fill="none" stroke="#000000" points="319,-258 319,-290 475,-290 475,-258 319,-258"/>
|
||||||
<text text-anchor="start" x="61.211" y="-271" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3P</text>
|
<text text-anchor="start" x="361.711" y="-271" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3P</text>
|
||||||
<polygon fill="none" stroke="#000000" points="18.5,-226 18.5,-258 174.5,-258 174.5,-226 18.5,-226"/>
|
<polygon fill="none" stroke="#000000" points="319,-226 319,-258 475,-258 475,-226 319,-226"/>
|
||||||
<text text-anchor="start" x="28.1525" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3P</text>
|
<text text-anchor="start" x="328.6525" y="-239" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3P</text>
|
||||||
<polygon fill="none" stroke="#000000" points="18.5,-194 18.5,-226 174.5,-226 174.5,-194 18.5,-194"/>
|
<polygon fill="none" stroke="#000000" points="319,-194 319,-226 475,-226 475,-194 319,-194"/>
|
||||||
<text text-anchor="start" x="81.5025" y="-207" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="382.0025" y="-207" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A6->A8 -->
|
<!-- A6->A8 -->
|
||||||
<g id="edge6" class="edge">
|
<g id="edge6" class="edge">
|
||||||
<title>A6->A8</title>
|
<title>A6->A8</title>
|
||||||
<path fill="none" stroke="#000000" d="M64.0865,-395.8051C71.6179,-360.0682 80.0195,-320.2015 86.3817,-290.0125"/>
|
<path fill="none" stroke="#000000" d="M435.1335,-395.8051C426.2731,-360.0682 416.3888,-320.2015 408.9039,-290.0125"/>
|
||||||
<polygon fill="none" stroke="#000000" points="60.6253,-395.2569 61.9878,-405.7637 67.4748,-396.7004 60.6253,-395.2569"/>
|
<polygon fill="none" stroke="#000000" points="431.7989,-396.8999 437.6026,-405.7637 438.5932,-395.2153 431.7989,-396.8999"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A7->A7 -->
|
<!-- A7->A7 -->
|
||||||
<g id="edge13" class="edge">
|
<g id="edge13" class="edge">
|
||||||
<title>A7->A7</title>
|
<title>A7->A7</title>
|
||||||
<path fill="none" stroke="#000000" d="M360.6684,-272.6238C371.3394,-267.6708 378.5,-257.4629 378.5,-242 378.5,-231.1277 374.9599,-222.8533 369.1486,-217.1769"/>
|
<path fill="none" stroke="#000000" d="M283.1684,-272.6238C293.8394,-267.6708 301,-257.4629 301,-242 301,-231.1277 297.4599,-222.8533 291.6486,-217.1769"/>
|
||||||
<polygon fill="#000000" stroke="#000000" points="360.6684,-211.3762 371.4628,-213.3079 364.7953,-214.1991 368.9222,-217.0221 368.9222,-217.0221 368.9222,-217.0221 364.7953,-214.1991 366.3816,-220.7363 360.6684,-211.3762 360.6684,-211.3762"/>
|
<polygon fill="#000000" stroke="#000000" points="283.1684,-211.3762 293.9628,-213.3079 287.2953,-214.1991 291.4222,-217.0221 291.4222,-217.0221 291.4222,-217.0221 287.2953,-214.1991 288.8816,-220.7363 283.1684,-211.3762 283.1684,-211.3762"/>
|
||||||
<text text-anchor="middle" x="380.4014" y="-211.6335" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">0..1</text>
|
<text text-anchor="middle" x="302.9014" y="-211.6335" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">0..1</text>
|
||||||
<text text-anchor="middle" x="372.7075" y="-253.6532" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">has</text>
|
<text text-anchor="middle" x="295.2075" y="-253.6532" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">has</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A11 -->
|
<!-- A11 -->
|
||||||
<g id="node12" class="node">
|
<g id="node12" class="node">
|
||||||
<title>A11</title>
|
<title>A11</title>
|
||||||
<polygon fill="none" stroke="#000000" points="306.5,-88 306.5,-120 428.5,-120 428.5,-88 306.5,-88"/>
|
<polygon fill="none" stroke="#000000" points="73,-88 73,-120 195,-120 195,-88 73,-88"/>
|
||||||
<text text-anchor="start" x="343.8845" y="-101" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InverterG3</text>
|
<text text-anchor="start" x="110.3845" y="-101" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InverterG3</text>
|
||||||
<polygon fill="none" stroke="#000000" points="306.5,-56 306.5,-88 428.5,-88 428.5,-56 306.5,-56"/>
|
<polygon fill="none" stroke="#000000" points="73,-56 73,-88 195,-88 195,-56 73,-56"/>
|
||||||
<text text-anchor="start" x="336.9355" y="-69" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__ha_restarts</text>
|
<text text-anchor="start" x="103.4355" y="-69" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__ha_restarts</text>
|
||||||
<polygon fill="none" stroke="#000000" points="306.5,0 306.5,-56 428.5,-56 428.5,0 306.5,0"/>
|
<polygon fill="none" stroke="#000000" points="73,0 73,-56 195,-56 195,0 73,0"/>
|
||||||
<text text-anchor="start" x="316.1035" y="-37" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">async_create_remote()</text>
|
<text text-anchor="start" x="82.6035" y="-37" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">async_create_remote()</text>
|
||||||
<text text-anchor="start" x="352.5025" y="-13" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="119.0025" y="-13" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A7->A11 -->
|
<!-- A7->A11 -->
|
||||||
<g id="edge12" class="edge">
|
<g id="edge12" class="edge">
|
||||||
<title>A7->A11</title>
|
<title>A7->A11</title>
|
||||||
<path fill="none" stroke="#000000" d="M311.4044,-184.5048C320.6254,-164.0387 331.0304,-140.9447 340.3527,-120.2539"/>
|
<path fill="none" stroke="#000000" d="M184.5103,-184.2281C176.2169,-163.8307 166.8737,-140.8515 158.4988,-120.2539"/>
|
||||||
<polygon fill="none" stroke="#000000" points="308.176,-183.1501 307.2592,-193.7052 314.5582,-186.0256 308.176,-183.1501"/>
|
<polygon fill="none" stroke="#000000" points="181.3548,-185.7599 188.3636,-193.7052 187.8393,-183.1233 181.3548,-185.7599"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A8->A8 -->
|
<!-- A8->A8 -->
|
||||||
<g id="edge15" class="edge">
|
<g id="edge15" class="edge">
|
||||||
<title>A8->A8</title>
|
<title>A8->A8</title>
|
||||||
<path fill="none" stroke="#000000" d="M174.8471,-272.2739C185.4443,-267.1987 192.5,-257.1074 192.5,-242 192.5,-231.3776 189.0118,-223.2351 183.2569,-217.5725"/>
|
<path fill="none" stroke="#000000" d="M475.3471,-272.2739C485.9443,-267.1987 493,-257.1074 493,-242 493,-231.3776 489.5118,-223.2351 483.7569,-217.5725"/>
|
||||||
<polygon fill="#000000" stroke="#000000" points="174.8471,-211.7261 185.6266,-213.7393 178.9525,-214.5802 183.0579,-217.4342 183.0579,-217.4342 183.0579,-217.4342 178.9525,-214.5802 180.4893,-221.1291 174.8471,-211.7261 174.8471,-211.7261"/>
|
<polygon fill="#000000" stroke="#000000" points="475.3471,-211.7261 486.1266,-213.7393 479.4525,-214.5802 483.5579,-217.4342 483.5579,-217.4342 483.5579,-217.4342 479.4525,-214.5802 480.9893,-221.1291 475.3471,-211.7261 475.3471,-211.7261"/>
|
||||||
<text text-anchor="middle" x="194.5548" y="-212.1325" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">0..1</text>
|
<text text-anchor="middle" x="495.0548" y="-212.1325" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">0..1</text>
|
||||||
<text text-anchor="middle" x="186.7174" y="-253.1774" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">has</text>
|
<text text-anchor="middle" x="487.2174" y="-253.1774" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">has</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A12 -->
|
<!-- A12 -->
|
||||||
<g id="node13" class="node">
|
<g id="node13" class="node">
|
||||||
<title>A12</title>
|
<title>A12</title>
|
||||||
<polygon fill="none" stroke="#000000" points="142.5,-88 142.5,-120 264.5,-120 264.5,-88 142.5,-88"/>
|
<polygon fill="none" stroke="#000000" points="274,-88 274,-120 396,-120 396,-88 274,-88"/>
|
||||||
<text text-anchor="start" x="176.55" y="-101" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InverterG3P</text>
|
<text text-anchor="start" x="308.05" y="-101" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InverterG3P</text>
|
||||||
<polygon fill="none" stroke="#000000" points="142.5,-56 142.5,-88 264.5,-88 264.5,-56 142.5,-56"/>
|
<polygon fill="none" stroke="#000000" points="274,-56 274,-88 396,-88 396,-56 274,-56"/>
|
||||||
<text text-anchor="start" x="172.9355" y="-69" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__ha_restarts</text>
|
<text text-anchor="start" x="304.4355" y="-69" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__ha_restarts</text>
|
||||||
<polygon fill="none" stroke="#000000" points="142.5,0 142.5,-56 264.5,-56 264.5,0 142.5,0"/>
|
<polygon fill="none" stroke="#000000" points="274,0 274,-56 396,-56 396,0 274,0"/>
|
||||||
<text text-anchor="start" x="152.1035" y="-37" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">async_create_remote()</text>
|
<text text-anchor="start" x="283.6035" y="-37" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">async_create_remote()</text>
|
||||||
<text text-anchor="start" x="188.5025" y="-13" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="320.0025" y="-13" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A8->A12 -->
|
<!-- A8->A12 -->
|
||||||
<g id="edge14" class="edge">
|
<g id="edge14" class="edge">
|
||||||
<title>A8->A12</title>
|
<title>A8->A12</title>
|
||||||
<path fill="none" stroke="#000000" d="M129.9773,-185.0573C142.0896,-164.4551 155.802,-141.1311 168.076,-120.2539"/>
|
<path fill="none" stroke="#000000" d="M377.3195,-184.2281C370.3709,-163.8307 362.5428,-140.8515 355.526,-120.2539"/>
|
||||||
<polygon fill="none" stroke="#000000" points="126.9441,-183.3107 124.8931,-193.7052 132.9785,-186.8585 126.9441,-183.3107"/>
|
<polygon fill="none" stroke="#000000" points="374.0102,-185.368 380.5479,-193.7052 380.6363,-183.1107 374.0102,-185.368"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A9 -->
|
<!-- A9 -->
|
||||||
<g id="node10" class="node">
|
<g id="node10" class="node">
|
||||||
<title>A9</title>
|
<title>A9</title>
|
||||||
<polygon fill="none" stroke="#000000" points="109.5,-572 109.5,-604 225.5,-604 225.5,-572 109.5,-572"/>
|
<polygon fill="none" stroke="#000000" points="277,-572 277,-604 393,-604 393,-572 277,-572"/>
|
||||||
<text text-anchor="start" x="137.774" y="-585" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">AsyncStream</text>
|
<text text-anchor="start" x="305.274" y="-585" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">AsyncStream</text>
|
||||||
<polygon fill="none" stroke="#000000" points="109.5,-492 109.5,-572 225.5,-572 225.5,-492 109.5,-492"/>
|
<polygon fill="none" stroke="#000000" points="277,-492 277,-572 393,-572 393,-492 277,-492"/>
|
||||||
<text text-anchor="start" x="153.053" y="-553" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">reader</text>
|
<text text-anchor="start" x="320.553" y="-553" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">reader</text>
|
||||||
<text text-anchor="start" x="155.283" y="-541" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">writer</text>
|
<text text-anchor="start" x="322.783" y="-541" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">writer</text>
|
||||||
<text text-anchor="start" x="157.497" y="-529" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">addr</text>
|
<text text-anchor="start" x="324.997" y="-529" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">addr</text>
|
||||||
<text text-anchor="start" x="153.053" y="-517" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">r_addr</text>
|
<text text-anchor="start" x="320.553" y="-517" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">r_addr</text>
|
||||||
<text text-anchor="start" x="153.608" y="-505" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">l_addr</text>
|
<text text-anchor="start" x="321.108" y="-505" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">l_addr</text>
|
||||||
<polygon fill="none" stroke="#000000" points="109.5,-364 109.5,-492 225.5,-492 225.5,-364 109.5,-364"/>
|
<polygon fill="none" stroke="#000000" points="277,-364 277,-492 393,-492 393,-364 277,-364"/>
|
||||||
<text text-anchor="start" x="119.1575" y="-473" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>server_loop()</text>
|
<text text-anchor="start" x="286.6575" y="-473" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>server_loop()</text>
|
||||||
<text text-anchor="start" x="121.378" y="-461" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>client_loop()</text>
|
<text text-anchor="start" x="288.878" y="-461" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>client_loop()</text>
|
||||||
<text text-anchor="start" x="139.154" y="-449" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>loop</text>
|
<text text-anchor="start" x="306.654" y="-449" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"><async>loop</text>
|
||||||
<text text-anchor="start" x="155.282" y="-437" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">disc()</text>
|
<text text-anchor="start" x="322.782" y="-437" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">disc()</text>
|
||||||
<text text-anchor="start" x="152.5025" y="-425" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
<text text-anchor="start" x="320.0025" y="-425" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
|
||||||
<text text-anchor="start" x="132.7705" y="-401" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_read()</text>
|
<text text-anchor="start" x="300.2705" y="-401" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_read()</text>
|
||||||
<text text-anchor="start" x="132.221" y="-389" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_write()</text>
|
<text text-anchor="start" x="299.721" y="-389" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_write()</text>
|
||||||
<text text-anchor="start" x="126.107" y="-377" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_forward()</text>
|
<text text-anchor="start" x="293.607" y="-377" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">__async_forward()</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- A9->A7 -->
|
<!-- A9->A7 -->
|
||||||
<g id="edge7" class="edge">
|
<g id="edge7" class="edge">
|
||||||
<title>A9->A7</title>
|
<title>A9->A7</title>
|
||||||
<path fill="none" stroke="#000000" d="M230.0233,-355.7743C241.4579,-332.3236 252.7154,-309.2362 262.0696,-290.0521"/>
|
<path fill="none" stroke="#000000" d="M272.2034,-364.3403C258.3698,-337.9803 244.4918,-311.5356 233.1958,-290.0108"/>
|
||||||
<polygon fill="none" stroke="#000000" points="226.8727,-354.25 225.6358,-364.7724 233.1645,-357.318 226.8727,-354.25"/>
|
<polygon fill="none" stroke="#000000" points="269.1431,-366.041 276.8893,-373.2693 275.3415,-362.7881 269.1431,-366.041"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A9->A8 -->
|
<!-- A9->A8 -->
|
||||||
<g id="edge8" class="edge">
|
<g id="edge8" class="edge">
|
||||||
<title>A9->A8</title>
|
<title>A9->A8</title>
|
||||||
<path fill="none" stroke="#000000" d="M129.3389,-353.9299C122.6561,-331.1516 116.0996,-308.8044 110.6242,-290.1415"/>
|
<path fill="none" stroke="#000000" d="M368.3237,-353.9299C374.1595,-331.1516 379.8848,-308.8044 384.6662,-290.1415"/>
|
||||||
<polygon fill="none" stroke="#000000" points="126.0343,-355.0988 132.208,-363.709 132.7512,-353.1281 126.0343,-355.0988"/>
|
<polygon fill="none" stroke="#000000" points="364.9098,-353.1532 365.8184,-363.709 371.6908,-354.8905 364.9098,-353.1532"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A10->A11 -->
|
<!-- A10->A11 -->
|
||||||
<g id="edge9" class="edge">
|
<g id="edge9" class="edge">
|
||||||
<title>A10->A11</title>
|
<title>A10->A11</title>
|
||||||
<path fill="none" stroke="#000000" d="M413.3164,-160.4648C407.1223,-146.8826 400.7985,-133.016 394.9051,-120.0931"/>
|
<path fill="none" stroke="#000000" d="M93.7037,-160.4648C99.1515,-146.8826 104.7134,-133.016 109.8967,-120.0931"/>
|
||||||
<polygon fill="none" stroke="#000000" points="410.2431,-162.1611 417.577,-169.8074 416.6121,-159.2566 410.2431,-162.1611"/>
|
<polygon fill="none" stroke="#000000" points="90.4307,-159.2232 89.9564,-169.8074 96.9276,-161.8291 90.4307,-159.2232"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- A10->A12 -->
|
<!-- A10->A12 -->
|
||||||
<g id="edge10" class="edge">
|
<g id="edge10" class="edge">
|
||||||
<title>A10->A12</title>
|
<title>A10->A12</title>
|
||||||
<path fill="none" stroke="#000000" d="M388.6131,-170.9072C388.2425,-170.6025 387.8715,-170.3001 387.5,-170 351.9061,-141.2441 336.8037,-143.4318 297.5,-120 286.7739,-113.6054 275.4857,-106.6243 264.5994,-99.7581"/>
|
<path fill="none" stroke="#000000" d="M122.8753,-170.8927C123.2497,-170.5927 123.6246,-170.2951 124,-170 149.1284,-150.2443 220.9786,-114.1777 273.8825,-88.7388"/>
|
||||||
<polygon fill="none" stroke="#000000" points="386.4756,-173.6866 396.3191,-177.6053 391.0678,-168.4034 386.4756,-173.6866"/>
|
<polygon fill="none" stroke="#000000" points="120.4501,-168.3606 115.1024,-177.5068 124.9865,-173.6918 120.4501,-168.3606"/>
|
||||||
|
</g>
|
||||||
|
<!-- A13 -->
|
||||||
|
<g id="node14" class="node">
|
||||||
|
<title>A13</title>
|
||||||
|
<polygon fill="none" stroke="#000000" points="350,-1164 350,-1196 453,-1196 453,-1164 350,-1164"/>
|
||||||
|
<text text-anchor="start" x="390.662" y="-1177" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Infos</text>
|
||||||
|
<polygon fill="none" stroke="#000000" points="350,-1108 350,-1164 453,-1164 453,-1108 350,-1108"/>
|
||||||
|
<text text-anchor="start" x="393.4415" y="-1145" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">stat</text>
|
||||||
|
<text text-anchor="start" x="368.986" y="-1133" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">new_stat_data</text>
|
||||||
|
<text text-anchor="start" x="382.6035" y="-1121" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">info_dev</text>
|
||||||
|
<polygon fill="none" stroke="#000000" points="350,-968 350,-1108 453,-1108 453,-968 350,-968"/>
|
||||||
|
<text text-anchor="start" x="377.3355" y="-1089" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">static_init()</text>
|
||||||
|
<text text-anchor="start" x="375.3845" y="-1077" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">dev_value()</text>
|
||||||
|
<text text-anchor="start" x="372.3305" y="-1065" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">inc_counter()</text>
|
||||||
|
<text text-anchor="start" x="370.6605" y="-1053" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">dec_counter()</text>
|
||||||
|
<text text-anchor="start" x="368.71" y="-1041" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ha_proxy_conf</text>
|
||||||
|
<text text-anchor="start" x="383.713" y="-1029" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ha_conf</text>
|
||||||
|
<text text-anchor="start" x="377.8745" y="-1017" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">update_db</text>
|
||||||
|
<text text-anchor="start" x="362.037" y="-1005" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">set_db_def_value</text>
|
||||||
|
<text text-anchor="start" x="371.4855" y="-993" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">get_db_value</text>
|
||||||
|
<text text-anchor="start" x="359.8225" y="-981" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ignore_this_device</text>
|
||||||
|
</g>
|
||||||
|
<!-- A14 -->
|
||||||
|
<g id="node15" class="node">
|
||||||
|
<title>A14</title>
|
||||||
|
<polygon fill="none" stroke="#000000" points="319,-802 319,-834 386,-834 386,-802 319,-802"/>
|
||||||
|
<text text-anchor="start" x="334.993" y="-815" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InfosG3</text>
|
||||||
|
<polygon fill="none" stroke="#000000" points="319,-782 319,-802 386,-802 386,-782 319,-782"/>
|
||||||
|
<polygon fill="none" stroke="#000000" points="319,-738 319,-782 386,-782 386,-738 319,-738"/>
|
||||||
|
<text text-anchor="start" x="328.884" y="-763" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ha_confs()</text>
|
||||||
|
<text text-anchor="start" x="336.668" y="-751" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">parse()</text>
|
||||||
|
</g>
|
||||||
|
<!-- A13->A14 -->
|
||||||
|
<g id="edge16" class="edge">
|
||||||
|
<title>A13->A14</title>
|
||||||
|
<path fill="none" stroke="#000000" d="M380.4486,-957.853C373.2314,-914.2551 365.5447,-867.821 359.9831,-834.2247"/>
|
||||||
|
<polygon fill="none" stroke="#000000" points="377.0391,-958.688 382.1254,-967.9821 383.9452,-957.5447 377.0391,-958.688"/>
|
||||||
|
</g>
|
||||||
|
<!-- A15 -->
|
||||||
|
<g id="node16" class="node">
|
||||||
|
<title>A15</title>
|
||||||
|
<polygon fill="none" stroke="#000000" points="417,-802 417,-834 484,-834 484,-802 417,-802"/>
|
||||||
|
<text text-anchor="start" x="429.6585" y="-815" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">InfosG3P</text>
|
||||||
|
<polygon fill="none" stroke="#000000" points="417,-782 417,-802 484,-802 484,-782 417,-782"/>
|
||||||
|
<polygon fill="none" stroke="#000000" points="417,-738 417,-782 484,-782 484,-738 417,-738"/>
|
||||||
|
<text text-anchor="start" x="426.884" y="-763" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ha_confs()</text>
|
||||||
|
<text text-anchor="start" x="434.668" y="-751" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">parse()</text>
|
||||||
|
</g>
|
||||||
|
<!-- A13->A15 -->
|
||||||
|
<g id="edge17" class="edge">
|
||||||
|
<title>A13->A15</title>
|
||||||
|
<path fill="none" stroke="#000000" d="M421.5514,-957.853C428.7686,-914.2551 436.4553,-867.821 442.0169,-834.2247"/>
|
||||||
|
<polygon fill="none" stroke="#000000" points="418.0548,-957.5447 419.8746,-967.9821 424.9609,-958.688 418.0548,-957.5447"/>
|
||||||
|
</g>
|
||||||
|
<!-- A14->A5 -->
|
||||||
|
<g id="edge19" class="edge">
|
||||||
|
<title>A14->A5</title>
|
||||||
|
<path fill="none" stroke="#000000" d="M328.0662,-737.8133C310.5801,-702.608 286.0413,-653.2031 263.2551,-607.3269"/>
|
||||||
|
<polygon fill="#000000" stroke="#000000" points="258.7238,-598.2039 267.2025,-605.1583 260.948,-602.682 263.1723,-607.1601 263.1723,-607.1601 263.1723,-607.1601 260.948,-602.682 259.142,-609.1619 258.7238,-598.2039 258.7238,-598.2039"/>
|
||||||
|
</g>
|
||||||
|
<!-- A15->A6 -->
|
||||||
|
<g id="edge18" class="edge">
|
||||||
|
<title>A15->A6</title>
|
||||||
|
<path fill="none" stroke="#000000" d="M451.1169,-737.8133C452.1468,-693.3826 453.7008,-626.3353 454.9531,-572.3076"/>
|
||||||
|
<polygon fill="#000000" stroke="#000000" points="455.1913,-562.0332 459.4583,-572.1349 455.0754,-567.0319 454.9595,-572.0306 454.9595,-572.0306 454.9595,-572.0306 455.0754,-567.0319 450.4607,-571.9262 455.1913,-562.0332 455.1913,-562.0332"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 27 KiB |
@@ -19,3 +19,8 @@
|
|||||||
[ConnectionG3]has-0..1>[ConnectionG3]
|
[ConnectionG3]has-0..1>[ConnectionG3]
|
||||||
[ConnectionG3P]^[InverterG3P]
|
[ConnectionG3P]^[InverterG3P]
|
||||||
[ConnectionG3P]has-0..1>[ConnectionG3P]
|
[ConnectionG3P]has-0..1>[ConnectionG3P]
|
||||||
|
[Infos|stat;new_stat_data;info_dev|static_init();dev_value();inc_counter();dec_counter();ha_proxy_conf;ha_conf;update_db;set_db_def_value;get_db_value;ignore_this_device]^[InfosG3||ha_confs();parse()]
|
||||||
|
[Infos]^[InfosG3P||ha_confs();parse()]
|
||||||
|
[InfosG3P]->[SolarmanV5]
|
||||||
|
[InfosG3]->[Talent]
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
aiomqtt==1.2.1
|
aiomqtt==2.0.0
|
||||||
schema==0.7.5
|
schema==0.7.5
|
||||||
|
aiocron==1.8
|
||||||
@@ -117,6 +117,7 @@ class AsyncStream():
|
|||||||
await self.remoteStream.__async_write()
|
await self.remoteStream.__async_write()
|
||||||
|
|
||||||
if self.remoteStream:
|
if self.remoteStream:
|
||||||
|
self.remoteStream._update_header(self._forward_buffer)
|
||||||
hex_dump_memory(logging.INFO,
|
hex_dump_memory(logging.INFO,
|
||||||
f'Forward to {self.remoteStream.addr}:',
|
f'Forward to {self.remoteStream.addr}:',
|
||||||
self._forward_buffer,
|
self._forward_buffer,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class Config():
|
|||||||
Get named parts of the config with get()'''
|
Get named parts of the config with get()'''
|
||||||
|
|
||||||
config = {}
|
config = {}
|
||||||
|
def_config = {}
|
||||||
conf_schema = Schema({
|
conf_schema = Schema({
|
||||||
'tsun': {
|
'tsun': {
|
||||||
'enabled': Use(bool),
|
'enabled': Use(bool),
|
||||||
@@ -45,7 +46,31 @@ class Config():
|
|||||||
if len(s) > 0 and
|
if len(s) > 0 and
|
||||||
s[-1] != '/' else s)),
|
s[-1] != '/' else s)),
|
||||||
|
|
||||||
Optional('suggested_area', default=""): Use(str)
|
Optional('suggested_area', default=""): Use(str),
|
||||||
|
Optional('pv1'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv2'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv3'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv4'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv5'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
},
|
||||||
|
Optional('pv6'): {
|
||||||
|
Optional('type'): Use(str),
|
||||||
|
Optional('manufacturer'): Use(str),
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
}, ignore_extra_keys=True
|
}, ignore_extra_keys=True
|
||||||
)
|
)
|
||||||
@@ -69,8 +94,16 @@ class Config():
|
|||||||
|
|
||||||
# overwrite the default values, with values from
|
# overwrite the default values, with values from
|
||||||
# the config.toml file
|
# the config.toml file
|
||||||
with open("config/config.toml", "rb") as f:
|
try:
|
||||||
usr_config = tomllib.load(f)
|
with open("config/config.toml", "rb") as f:
|
||||||
|
usr_config = tomllib.load(f)
|
||||||
|
except Exception as error:
|
||||||
|
logging.error(f'Config.read: {error}')
|
||||||
|
logging.info(
|
||||||
|
'\n To create the missing config.toml file, '
|
||||||
|
'you can rename the template config.example.toml\n'
|
||||||
|
' and customize it for your scenario.\n')
|
||||||
|
usr_config = def_config
|
||||||
|
|
||||||
config['tsun'] = def_config['tsun'] | usr_config['tsun']
|
config['tsun'] = def_config['tsun'] | usr_config['tsun']
|
||||||
config['solarman'] = def_config['solarman'] | \
|
config['solarman'] = def_config['solarman'] | \
|
||||||
@@ -81,6 +114,7 @@ class Config():
|
|||||||
usr_config['inverters']
|
usr_config['inverters']
|
||||||
|
|
||||||
cls.config = cls.conf_schema.validate(config)
|
cls.config = cls.conf_schema.validate(config)
|
||||||
|
cls.def_config = cls.conf_schema.validate(def_config)
|
||||||
# logging.debug(f'Readed config: "{cls.config}" ')
|
# logging.debug(f'Readed config: "{cls.config}" ')
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
@@ -96,3 +130,9 @@ class Config():
|
|||||||
return cls.config.get(member, {})
|
return cls.config.get(member, {})
|
||||||
else:
|
else:
|
||||||
return cls.config
|
return cls.config
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_default(cls, member: str) -> bool:
|
||||||
|
'''Check if the member is the default value'''
|
||||||
|
|
||||||
|
return cls.config.get(member) == cls.def_config.get(member)
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class InfosG3(Infos):
|
|||||||
update = False
|
update = False
|
||||||
name = str(f'info-id.0x{addr:x}')
|
name = str(f'info-id.0x{addr:x}')
|
||||||
|
|
||||||
self.tracer.log(level, f'{name} : {result}{unit}'
|
self.tracer.log(level, f'GEN3: {name} : {result}{unit}'
|
||||||
f' update: {update}')
|
f' update: {update}')
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ class InverterG3(Inverter, ConnectionG3):
|
|||||||
await self.mqtt.publish(f"{self.discovery_prfx}{component}"
|
await self.mqtt.publish(f"{self.discovery_prfx}{component}"
|
||||||
f"/{node_id}{id}/config", data_json)
|
f"/{node_id}{id}/config", data_json)
|
||||||
|
|
||||||
|
self.db.reg_clr_at_midnight(f'{self.entity_prfx}{node_id}')
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
logging.debug(f'InverterG3.close() l{self.l_addr} | r{self.r_addr}')
|
logging.debug(f'InverterG3.close() l{self.l_addr} | r{self.r_addr}')
|
||||||
super().close() # call close handler in the parent class
|
super().close() # call close handler in the parent class
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class Talent(Message):
|
|||||||
# deallocated by the garbage collector ==> we get a memory leak
|
# deallocated by the garbage collector ==> we get a memory leak
|
||||||
self.switch.clear()
|
self.switch.clear()
|
||||||
|
|
||||||
def set_serial_no(self, serial_no: str):
|
def __set_serial_no(self, serial_no: str):
|
||||||
|
|
||||||
if self.unique_id == serial_no:
|
if self.unique_id == serial_no:
|
||||||
logger.debug(f'SerialNo: {serial_no}')
|
logger.debug(f'SerialNo: {serial_no}')
|
||||||
@@ -72,6 +72,7 @@ class Talent(Message):
|
|||||||
self.node_id = inv['node_id']
|
self.node_id = inv['node_id']
|
||||||
self.sug_area = inv['suggested_area']
|
self.sug_area = inv['suggested_area']
|
||||||
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
||||||
|
self.db.set_pv_module_details(inv)
|
||||||
else:
|
else:
|
||||||
self.node_id = ''
|
self.node_id = ''
|
||||||
self.sug_area = ''
|
self.sug_area = ''
|
||||||
@@ -95,7 +96,7 @@ class Talent(Message):
|
|||||||
hex_dump_memory(logging.INFO, f'Received from {self.addr}:',
|
hex_dump_memory(logging.INFO, f'Received from {self.addr}:',
|
||||||
self._recv_buffer, self.header_len+self.data_len)
|
self._recv_buffer, self.header_len+self.data_len)
|
||||||
|
|
||||||
self.set_serial_no(self.id_str.decode("utf-8"))
|
self.__set_serial_no(self.id_str.decode("utf-8"))
|
||||||
self.__dispatch_msg()
|
self.__dispatch_msg()
|
||||||
self.__flush_recv_msg()
|
self.__flush_recv_msg()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -117,5 +117,5 @@ class InfosG3P(Infos):
|
|||||||
name = str(f'info-id.0x{addr:x}')
|
name = str(f'info-id.0x{addr:x}')
|
||||||
update = False
|
update = False
|
||||||
|
|
||||||
self.tracer.log(level, f'{name} : {result}{unit}'
|
self.tracer.log(level, f'GEN3PLUS: {name} : {result}{unit}'
|
||||||
f' update: {update}')
|
f' update: {update}')
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ class InverterG3P(Inverter, ConnectionG3P):
|
|||||||
await self.mqtt.publish(f"{self.discovery_prfx}{component}"
|
await self.mqtt.publish(f"{self.discovery_prfx}{component}"
|
||||||
f"/{node_id}{id}/config", data_json)
|
f"/{node_id}{id}/config", data_json)
|
||||||
|
|
||||||
|
self.db.reg_clr_at_midnight(f'{self.entity_prfx}{node_id}')
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
logging.debug(f'InverterG3P.close() l{self.l_addr} | r{self.r_addr}')
|
logging.debug(f'InverterG3P.close() l{self.l_addr} | r{self.r_addr}')
|
||||||
super().close() # call close handler in the parent class
|
super().close() # call close handler in the parent class
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import struct
|
import struct
|
||||||
# import json
|
# import json
|
||||||
import logging
|
import logging
|
||||||
# import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
if __name__ == "app.src.gen3plus.solarman_v5":
|
if __name__ == "app.src.gen3plus.solarman_v5":
|
||||||
@@ -19,6 +19,32 @@ else: # pragma: no cover
|
|||||||
logger = logging.getLogger('msg')
|
logger = logging.getLogger('msg')
|
||||||
|
|
||||||
|
|
||||||
|
class Sequence():
|
||||||
|
def __init__(self, server_side: bool):
|
||||||
|
self.rcv_idx = 0
|
||||||
|
self.snd_idx = 0
|
||||||
|
self.server_side = server_side
|
||||||
|
|
||||||
|
def set_recv(self, val: int):
|
||||||
|
if self.server_side:
|
||||||
|
self.rcv_idx = val >> 8
|
||||||
|
self.snd_idx = val & 0xff
|
||||||
|
else:
|
||||||
|
self.rcv_idx = val & 0xff
|
||||||
|
self.snd_idx = val >> 8
|
||||||
|
|
||||||
|
def get_send(self):
|
||||||
|
self.snd_idx += 1
|
||||||
|
self.snd_idx &= 0xff
|
||||||
|
if self.server_side:
|
||||||
|
return (self.rcv_idx << 8) | self.snd_idx
|
||||||
|
else:
|
||||||
|
return (self.snd_idx << 8) | self.rcv_idx
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.rcv_idx:02x}:{self.snd_idx:02x}'
|
||||||
|
|
||||||
|
|
||||||
class SolarmanV5(Message):
|
class SolarmanV5(Message):
|
||||||
|
|
||||||
def __init__(self, server_side: bool):
|
def __init__(self, server_side: bool):
|
||||||
@@ -26,16 +52,16 @@ class SolarmanV5(Message):
|
|||||||
|
|
||||||
self.header_len = 11 # overwrite construcor in class Message
|
self.header_len = 11 # overwrite construcor in class Message
|
||||||
self.control = 0
|
self.control = 0
|
||||||
self.serial = 0
|
self.seq = Sequence(server_side)
|
||||||
self.snr = 0
|
self.snr = 0
|
||||||
self.db = InfosG3P()
|
self.db = InfosG3P()
|
||||||
self.switch = {
|
self.switch = {
|
||||||
|
|
||||||
0x4210: self.msg_data_ind, # real time data
|
0x4210: self.msg_data_ind, # real time data
|
||||||
0x1210: self.msg_data_rsp, # at least every 5 minutes
|
0x1210: self.msg_response, # at least every 5 minutes
|
||||||
|
|
||||||
0x4710: self.msg_hbeat_ind, # heatbeat
|
0x4710: self.msg_hbeat_ind, # heatbeat
|
||||||
0x1710: self.msg_hbeat_rsp, # every 2 minutes
|
0x1710: self.msg_response, # every 2 minutes
|
||||||
|
|
||||||
# every 3 hours comes a sync seuqence:
|
# every 3 hours comes a sync seuqence:
|
||||||
# 00:00:00 0x4110 device data ftype: 0x02
|
# 00:00:00 0x4110 device data ftype: 0x02
|
||||||
@@ -46,18 +72,18 @@ class SolarmanV5(Message):
|
|||||||
# 00:00:07 0x4310 wifi data ftype: 0x01 sub-id 0x0018: 0c # noqa: E501
|
# 00:00:07 0x4310 wifi data ftype: 0x01 sub-id 0x0018: 0c # noqa: E501
|
||||||
# 00:00:08 0x4810 options? ftype: 0x01
|
# 00:00:08 0x4810 options? ftype: 0x01
|
||||||
|
|
||||||
0x4110: self.msg_dev_ind, # device data, sync start
|
0x4110: self.msg_dev_ind, # device data, sync start
|
||||||
0x1110: self.msg_dev_rsp, # every 3 hours
|
0x1110: self.msg_response, # every 3 hours
|
||||||
|
|
||||||
0x4310: self.msg_forward, # regulary after 3-6 hours
|
0x4310: self.msg_sync_start, # regulary after 3-6 hours
|
||||||
0x1310: self.msg_forward,
|
0x1310: self.msg_response,
|
||||||
0x4810: self.msg_forward, # sync end
|
0x4810: self.msg_sync_end, # sync end
|
||||||
0x1810: self.msg_forward,
|
0x1810: self.msg_response,
|
||||||
|
|
||||||
#
|
#
|
||||||
# AT cmd
|
# MODbus or AT cmd
|
||||||
0x4510: self.at_command_ind, # from server
|
0x4510: self.msg_command_req, # from server
|
||||||
0x1510: self.msg_forward, # from inverter
|
0x1510: self.msg_response, # from inverter
|
||||||
}
|
}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -70,7 +96,7 @@ class SolarmanV5(Message):
|
|||||||
# deallocated by the garbage collector ==> we get a memory leak
|
# deallocated by the garbage collector ==> we get a memory leak
|
||||||
self.switch.clear()
|
self.switch.clear()
|
||||||
|
|
||||||
def set_serial_no(self, snr: int):
|
def __set_serial_no(self, snr: int):
|
||||||
serial_no = str(snr)
|
serial_no = str(snr)
|
||||||
if self.unique_id == serial_no:
|
if self.unique_id == serial_no:
|
||||||
logger.debug(f'SerialNo: {serial_no}')
|
logger.debug(f'SerialNo: {serial_no}')
|
||||||
@@ -87,6 +113,7 @@ class SolarmanV5(Message):
|
|||||||
self.node_id = inv['node_id']
|
self.node_id = inv['node_id']
|
||||||
self.sug_area = inv['suggested_area']
|
self.sug_area = inv['suggested_area']
|
||||||
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
logger.debug(f'SerialNo {serial_no} allowed! area:{self.sug_area}') # noqa: E501
|
||||||
|
self.db.set_pv_module_details(inv)
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
self.node_id = ''
|
self.node_id = ''
|
||||||
@@ -112,7 +139,7 @@ class SolarmanV5(Message):
|
|||||||
self._recv_buffer, self.header_len+self.data_len+2)
|
self._recv_buffer, self.header_len+self.data_len+2)
|
||||||
if self.__trailer_is_ok(self._recv_buffer, self.header_len
|
if self.__trailer_is_ok(self._recv_buffer, self.header_len
|
||||||
+ self.data_len + 2):
|
+ self.data_len + 2):
|
||||||
self.set_serial_no(self.snr)
|
self.__set_serial_no(self.snr)
|
||||||
self.__dispatch_msg()
|
self.__dispatch_msg()
|
||||||
self.__flush_recv_msg()
|
self.__flush_recv_msg()
|
||||||
return
|
return
|
||||||
@@ -159,6 +186,13 @@ class SolarmanV5(Message):
|
|||||||
type += 'S'
|
type += 'S'
|
||||||
return switch.get(type, '???')
|
return switch.get(type, '???')
|
||||||
|
|
||||||
|
def _timestamp(self): # pragma: no cover
|
||||||
|
# utc as epoche
|
||||||
|
return int(time.time())
|
||||||
|
|
||||||
|
def _heartbeat(self) -> int:
|
||||||
|
return 60
|
||||||
|
|
||||||
def __parse_header(self, buf: bytes, buf_len: int) -> None:
|
def __parse_header(self, buf: bytes, buf_len: int) -> None:
|
||||||
|
|
||||||
if (buf_len < self.header_len): # enough bytes for complete header?
|
if (buf_len < self.header_len): # enough bytes for complete header?
|
||||||
@@ -167,10 +201,10 @@ class SolarmanV5(Message):
|
|||||||
result = struct.unpack_from('<BHHHL', buf, 0)
|
result = struct.unpack_from('<BHHHL', buf, 0)
|
||||||
|
|
||||||
# store parsed header values in the class
|
# store parsed header values in the class
|
||||||
start = result[0] # len of complete message
|
start = result[0] # start byte
|
||||||
self.data_len = result[1] # len of variable id string
|
self.data_len = result[1] # len of variable id string
|
||||||
self.control = result[2]
|
self.control = result[2]
|
||||||
self.serial = result[3]
|
self.seq.set_recv(result[3])
|
||||||
self.snr = result[4]
|
self.snr = result[4]
|
||||||
|
|
||||||
if start != 0xA5:
|
if start != 0xA5:
|
||||||
@@ -193,6 +227,7 @@ class SolarmanV5(Message):
|
|||||||
self._recv_buffer = bytearray()
|
self._recv_buffer = bytearray()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
check = sum(buf[1:buf_len-2]) & 0xff
|
check = sum(buf[1:buf_len-2]) & 0xff
|
||||||
if check != crc:
|
if check != crc:
|
||||||
self.inc_counter('Invalid_Msg_Format')
|
self.inc_counter('Invalid_Msg_Format')
|
||||||
@@ -203,6 +238,36 @@ class SolarmanV5(Message):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def __build_header(self, ctrl) -> None:
|
||||||
|
'''build header for new transmit message'''
|
||||||
|
self.send_msg_ofs = len(self._send_buffer)
|
||||||
|
|
||||||
|
self._send_buffer += struct.pack(
|
||||||
|
'<BHHHL', 0xA5, 0, ctrl, self.seq.get_send(), self.snr)
|
||||||
|
fnc = self.switch.get(ctrl, self.msg_unknown)
|
||||||
|
logger.info(self.__flow_str(self.server_side, 'tx') +
|
||||||
|
f' Ctl: {int(ctrl):#04x} Msg: {fnc.__name__!r}')
|
||||||
|
|
||||||
|
def __finish_send_msg(self) -> None:
|
||||||
|
'''finish the transmit message, set lenght and checksum'''
|
||||||
|
_len = len(self._send_buffer) - self.send_msg_ofs
|
||||||
|
struct.pack_into('<H', self._send_buffer, self.send_msg_ofs+1, _len-11)
|
||||||
|
check = sum(self._send_buffer[self.send_msg_ofs+1:self.send_msg_ofs +
|
||||||
|
_len]) & 0xff
|
||||||
|
self._send_buffer += struct.pack('<BB', check, 0x15) # crc & stop
|
||||||
|
|
||||||
|
def _update_header(self, _forward_buffer):
|
||||||
|
'''update header for message before forwarding,
|
||||||
|
set sequence and checksum'''
|
||||||
|
_len = len(_forward_buffer)
|
||||||
|
struct.pack_into('<H', _forward_buffer, 1,
|
||||||
|
_len-13)
|
||||||
|
struct.pack_into('<H', _forward_buffer, 5,
|
||||||
|
self.seq.get_send())
|
||||||
|
|
||||||
|
check = sum(_forward_buffer[1:_len-2]) & 0xff
|
||||||
|
struct.pack_into('<B', _forward_buffer, _len-2, check)
|
||||||
|
|
||||||
def __dispatch_msg(self) -> None:
|
def __dispatch_msg(self) -> None:
|
||||||
fnc = self.switch.get(self.control, self.msg_unknown)
|
fnc = self.switch.get(self.control, self.msg_unknown)
|
||||||
if self.unique_id:
|
if self.unique_id:
|
||||||
@@ -219,87 +284,22 @@ class SolarmanV5(Message):
|
|||||||
self._recv_buffer = self._recv_buffer[(self.header_len +
|
self._recv_buffer = self._recv_buffer[(self.header_len +
|
||||||
self.data_len+2):]
|
self.data_len+2):]
|
||||||
self.header_valid = False
|
self.header_valid = False
|
||||||
'''
|
|
||||||
def modbus(self, data):
|
|
||||||
POLY = 0xA001
|
|
||||||
|
|
||||||
crc = 0xFFFF
|
def __send_ack_rsp(self, msgtype, ftype, ack=1):
|
||||||
for byte in data:
|
self.__build_header(msgtype)
|
||||||
crc ^= byte
|
self._send_buffer += struct.pack('<BBLL', ftype, ack,
|
||||||
for _ in range(8):
|
self._timestamp(),
|
||||||
crc = ((crc >> 1) ^ POLY
|
self._heartbeat())
|
||||||
if (crc & 0x0001)
|
self.__finish_send_msg()
|
||||||
else crc >> 1)
|
|
||||||
return crc
|
|
||||||
|
|
||||||
def validate_modbus_crc(self, frame):
|
def send_at_cmd(self, AT_cmd: str) -> None:
|
||||||
# Calculate crc with all but the last 2 bytes of
|
self.__build_header(0x4510)
|
||||||
# the frame (they contain the crc)
|
self._send_buffer += struct.pack(f'<BHLLL{len(AT_cmd)}sc', 1, 2,
|
||||||
calc_crc = 0xFFFF
|
0, 0, 0, AT_cmd.encode('utf-8'),
|
||||||
for pos in frame[:-2]:
|
b'\r')
|
||||||
calc_crc ^= pos
|
self.__finish_send_msg()
|
||||||
for i in range(8):
|
|
||||||
if (calc_crc & 1) != 0:
|
|
||||||
calc_crc >>= 1
|
|
||||||
calc_crc ^= 0xA001 # bitwise 'or' with modbus magic
|
|
||||||
# number (0xa001 == bitwise
|
|
||||||
# reverse of 0x8005)
|
|
||||||
else:
|
|
||||||
calc_crc >>= 1
|
|
||||||
|
|
||||||
# Compare calculated crc with the one supplied in the frame....
|
def __forward_msg(self):
|
||||||
frame_crc, = struct.unpack('<H', frame[-2:])
|
|
||||||
if calc_crc == frame_crc:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
'''
|
|
||||||
'''
|
|
||||||
Message handler methods
|
|
||||||
'''
|
|
||||||
def msg_unknown(self):
|
|
||||||
logger.warning(f"Unknow Msg: ID:{int(self.control):#04x}")
|
|
||||||
self.inc_counter('Unknown_Msg')
|
|
||||||
self.msg_forward()
|
|
||||||
|
|
||||||
def msg_forward(self):
|
|
||||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
|
||||||
|
|
||||||
def msg_dev_ind(self):
|
|
||||||
data = self._recv_buffer[self.header_len:]
|
|
||||||
result = struct.unpack_from('<BLLL', data, 0)
|
|
||||||
ftype = result[0] # always 2
|
|
||||||
total = result[1]
|
|
||||||
tim = result[2]
|
|
||||||
res = result[3] # always zero
|
|
||||||
logger.info(f'frame type:{ftype:02x} total:{total}s'
|
|
||||||
f' timer:{tim:08x}s null:{res}')
|
|
||||||
dt = datetime.fromtimestamp(total)
|
|
||||||
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
|
||||||
|
|
||||||
self.__process_data(ftype)
|
|
||||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
|
||||||
|
|
||||||
def msg_dev_rsp(self):
|
|
||||||
self.msg_response()
|
|
||||||
|
|
||||||
def msg_data_ind(self):
|
|
||||||
data = self._recv_buffer
|
|
||||||
result = struct.unpack_from('<BLLLLL', data, self.header_len)
|
|
||||||
ftype = result[0] # 1 or 0x81
|
|
||||||
total = result[1]
|
|
||||||
tim = result[2]
|
|
||||||
offset = result[3]
|
|
||||||
unkn = result[4]
|
|
||||||
cnt = result[5]
|
|
||||||
logger.info(f'ftype:{ftype:02x} total:{total}s'
|
|
||||||
f' timer:{tim:08x}s ofs:{offset}'
|
|
||||||
f' ??: {unkn:08x} cnt:{cnt}')
|
|
||||||
dt = datetime.fromtimestamp(total)
|
|
||||||
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
|
||||||
|
|
||||||
ftype &= 0x7f # mask bit 7 (0x80)
|
|
||||||
self.__process_data(ftype)
|
|
||||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||||
|
|
||||||
def __process_data(self, ftype):
|
def __process_data(self, ftype):
|
||||||
@@ -337,14 +337,81 @@ class SolarmanV5(Message):
|
|||||||
Model = Version.split('_')[0]
|
Model = Version.split('_')[0]
|
||||||
self.db.set_db_def_value(Register.CHIP_MODEL, Model)
|
self.db.set_db_def_value(Register.CHIP_MODEL, Model)
|
||||||
|
|
||||||
def msg_data_rsp(self):
|
'''
|
||||||
self.msg_response()
|
Message handler methods
|
||||||
|
'''
|
||||||
|
def msg_unknown(self):
|
||||||
|
logger.warning(f"Unknow Msg: ID:{int(self.control):#04x}")
|
||||||
|
self.inc_counter('Unknown_Msg')
|
||||||
|
self.__forward_msg()
|
||||||
|
|
||||||
|
def msg_dev_ind(self):
|
||||||
|
data = self._recv_buffer[self.header_len:]
|
||||||
|
result = struct.unpack_from('<BLLL', data, 0)
|
||||||
|
ftype = result[0] # always 2
|
||||||
|
total = result[1]
|
||||||
|
tim = result[2]
|
||||||
|
res = result[3] # always zero
|
||||||
|
logger.info(f'frame type:{ftype:02x} total:{total}s'
|
||||||
|
f' timer:{tim:08x}s null:{res}')
|
||||||
|
dt = datetime.fromtimestamp(total)
|
||||||
|
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
||||||
|
|
||||||
|
self.__process_data(ftype)
|
||||||
|
self.__forward_msg()
|
||||||
|
self.__send_ack_rsp(0x1110, ftype)
|
||||||
|
|
||||||
|
def msg_data_ind(self):
|
||||||
|
data = self._recv_buffer
|
||||||
|
result = struct.unpack_from('<BLLLLL', data, self.header_len)
|
||||||
|
ftype = result[0] # 1 or 0x81
|
||||||
|
total = result[1]
|
||||||
|
tim = result[2]
|
||||||
|
offset = result[3]
|
||||||
|
unkn = result[4]
|
||||||
|
cnt = result[5]
|
||||||
|
logger.info(f'ftype:{ftype:02x} total:{total}s'
|
||||||
|
f' timer:{tim:08x}s ofs:{offset}'
|
||||||
|
f' ??: {unkn:08x} cnt:{cnt}')
|
||||||
|
dt = datetime.fromtimestamp(total)
|
||||||
|
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
||||||
|
|
||||||
|
self.__process_data(ftype & 0x7f) # mask bit 7 (0x80)
|
||||||
|
self.__forward_msg()
|
||||||
|
self.__send_ack_rsp(0x1210, ftype)
|
||||||
|
|
||||||
|
def msg_sync_start(self):
|
||||||
|
data = self._recv_buffer[self.header_len:]
|
||||||
|
result = struct.unpack_from('<B', data, 0)
|
||||||
|
ftype = result[0]
|
||||||
|
|
||||||
|
self.__forward_msg()
|
||||||
|
self.__send_ack_rsp(0x1310, ftype)
|
||||||
|
|
||||||
|
def msg_command_req(self):
|
||||||
|
data = self._recv_buffer[self.header_len:]
|
||||||
|
result = struct.unpack_from('<B', data, 0)
|
||||||
|
ftype = result[0]
|
||||||
|
|
||||||
|
self.inc_counter('AT_Command')
|
||||||
|
self.__forward_msg()
|
||||||
|
self.__send_ack_rsp(0x1510, ftype)
|
||||||
|
|
||||||
def msg_hbeat_ind(self):
|
def msg_hbeat_ind(self):
|
||||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
data = self._recv_buffer[self.header_len:]
|
||||||
|
result = struct.unpack_from('<B', data, 0)
|
||||||
|
ftype = result[0]
|
||||||
|
|
||||||
def msg_hbeat_rsp(self):
|
self.__forward_msg()
|
||||||
self.msg_response()
|
self.__send_ack_rsp(0x1710, ftype)
|
||||||
|
|
||||||
|
def msg_sync_end(self):
|
||||||
|
data = self._recv_buffer[self.header_len:]
|
||||||
|
result = struct.unpack_from('<B', data, 0)
|
||||||
|
ftype = result[0]
|
||||||
|
|
||||||
|
self.__forward_msg()
|
||||||
|
self.__send_ack_rsp(0x1810, ftype)
|
||||||
|
|
||||||
def msg_response(self):
|
def msg_response(self):
|
||||||
data = self._recv_buffer[self.header_len:]
|
data = self._recv_buffer[self.header_len:]
|
||||||
@@ -352,14 +419,9 @@ class SolarmanV5(Message):
|
|||||||
ftype = result[0] # always 2
|
ftype = result[0] # always 2
|
||||||
valid = result[1] == 1 # status
|
valid = result[1] == 1 # status
|
||||||
ts = result[2]
|
ts = result[2]
|
||||||
repeat = result[3] # always 60
|
set_hb = result[3] # always 60 or 120
|
||||||
logger.info(f'ftype:{ftype} accepted:{valid}'
|
logger.info(f'ftype:{ftype} accepted:{valid}'
|
||||||
f' ts:{ts:08x} repeat:{repeat}s')
|
f' ts:{ts:08x} nextHeartbeat: {set_hb}s')
|
||||||
|
|
||||||
dt = datetime.fromtimestamp(ts)
|
dt = datetime.fromtimestamp(ts)
|
||||||
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
||||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
|
||||||
|
|
||||||
def at_command_ind(self):
|
|
||||||
self.inc_counter('AT_Command')
|
|
||||||
self.msg_forward()
|
|
||||||
|
|||||||
153
app/src/infos.py
153
app/src/infos.py
@@ -34,21 +34,33 @@ class Register(Enum):
|
|||||||
PV1_VOLTAGE = 100
|
PV1_VOLTAGE = 100
|
||||||
PV1_CURRENT = 101
|
PV1_CURRENT = 101
|
||||||
PV1_POWER = 102
|
PV1_POWER = 102
|
||||||
|
PV1_MANUFACTURER = 103
|
||||||
|
PV1_MODEL = 104
|
||||||
PV2_VOLTAGE = 110
|
PV2_VOLTAGE = 110
|
||||||
PV2_CURRENT = 111
|
PV2_CURRENT = 111
|
||||||
PV2_POWER = 112
|
PV2_POWER = 112
|
||||||
|
PV2_MANUFACTURER = 113
|
||||||
|
PV2_MODEL = 114
|
||||||
PV3_VOLTAGE = 120
|
PV3_VOLTAGE = 120
|
||||||
PV3_CURRENT = 121
|
PV3_CURRENT = 121
|
||||||
PV3_POWER = 122
|
PV3_POWER = 122
|
||||||
|
PV3_MANUFACTURER = 123
|
||||||
|
PV3_MODEL = 124
|
||||||
PV4_VOLTAGE = 130
|
PV4_VOLTAGE = 130
|
||||||
PV4_CURRENT = 131
|
PV4_CURRENT = 131
|
||||||
PV4_POWER = 132
|
PV4_POWER = 132
|
||||||
|
PV4_MANUFACTURER = 133
|
||||||
|
PV4_MODEL = 134
|
||||||
PV5_VOLTAGE = 140
|
PV5_VOLTAGE = 140
|
||||||
PV5_CURRENT = 141
|
PV5_CURRENT = 141
|
||||||
PV5_POWER = 142
|
PV5_POWER = 142
|
||||||
|
PV5_MANUFACTURER = 143
|
||||||
|
PV5_MODEL = 144
|
||||||
PV6_VOLTAGE = 150
|
PV6_VOLTAGE = 150
|
||||||
PV6_CURRENT = 151
|
PV6_CURRENT = 151
|
||||||
PV6_POWER = 152
|
PV6_POWER = 152
|
||||||
|
PV6_MANUFACTURER = 153
|
||||||
|
PV6_MODEL = 154
|
||||||
PV1_DAILY_GENERATION = 200
|
PV1_DAILY_GENERATION = 200
|
||||||
PV1_TOTAL_GENERATION = 201
|
PV1_TOTAL_GENERATION = 201
|
||||||
PV2_DAILY_GENERATION = 210
|
PV2_DAILY_GENERATION = 210
|
||||||
@@ -95,6 +107,34 @@ class Register(Enum):
|
|||||||
TEST_REG2 = 10001
|
TEST_REG2 = 10001
|
||||||
|
|
||||||
|
|
||||||
|
class ClrAtMidnight:
|
||||||
|
__clr_at_midnight = [Register.PV1_DAILY_GENERATION, Register.PV2_DAILY_GENERATION, Register.PV3_DAILY_GENERATION, Register.PV4_DAILY_GENERATION, Register.PV5_DAILY_GENERATION, Register.PV6_DAILY_GENERATION, Register.DAILY_GENERATION] # noqa: E501
|
||||||
|
db = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add(cls, keys: list, prfx: str, reg: Register) -> None:
|
||||||
|
if reg not in cls.__clr_at_midnight:
|
||||||
|
return
|
||||||
|
|
||||||
|
prfx += f'{keys[0]}'
|
||||||
|
dict = cls.db
|
||||||
|
if prfx not in dict:
|
||||||
|
dict[prfx] = {}
|
||||||
|
dict = dict[prfx]
|
||||||
|
|
||||||
|
for key in keys[1:-1]:
|
||||||
|
if key not in dict:
|
||||||
|
dict[key] = {}
|
||||||
|
dict = dict[key]
|
||||||
|
dict[keys[-1]] = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def elm(cls) -> Generator[tuple[str, dict], None, None]:
|
||||||
|
for reg, name in cls.db.items():
|
||||||
|
yield reg, name
|
||||||
|
cls.db = {}
|
||||||
|
|
||||||
|
|
||||||
class Infos:
|
class Infos:
|
||||||
stat = {}
|
stat = {}
|
||||||
app_name = os.getenv('SERVICE_NAME', 'proxy')
|
app_name = os.getenv('SERVICE_NAME', 'proxy')
|
||||||
@@ -125,10 +165,12 @@ class Infos:
|
|||||||
'proxy': {'singleton': True, 'name': 'Proxy', 'mf': 'Stefan Allius'}, # noqa: E501
|
'proxy': {'singleton': True, 'name': 'Proxy', 'mf': 'Stefan Allius'}, # noqa: E501
|
||||||
'controller': {'via': 'proxy', 'name': 'Controller', 'mdl': Register.CHIP_MODEL, 'mf': Register.CHIP_TYPE, 'sw': Register.COLLECTOR_FW_VERSION}, # noqa: E501
|
'controller': {'via': 'proxy', 'name': 'Controller', 'mdl': Register.CHIP_MODEL, 'mf': Register.CHIP_TYPE, 'sw': Register.COLLECTOR_FW_VERSION}, # noqa: E501
|
||||||
'inverter': {'via': 'controller', 'name': 'Micro Inverter', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION}, # noqa: E501
|
'inverter': {'via': 'controller', 'name': 'Micro Inverter', 'mdl': Register.EQUIPMENT_MODEL, 'mf': Register.MANUFACTURER, 'sw': Register.VERSION}, # noqa: E501
|
||||||
'input_pv1': {'via': 'inverter', 'name': 'Module PV1'},
|
'input_pv1': {'via': 'inverter', 'name': 'Module PV1', 'mdl': Register.PV1_MODEL, 'mf': Register.PV1_MANUFACTURER}, # noqa: E501
|
||||||
'input_pv2': {'via': 'inverter', 'name': 'Module PV2', 'dep': {'reg': Register.NO_INPUTS, 'gte': 2}}, # noqa: E501
|
'input_pv2': {'via': 'inverter', 'name': 'Module PV2', 'mdl': Register.PV2_MODEL, 'mf': Register.PV2_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 2}}, # noqa: E501
|
||||||
'input_pv3': {'via': 'inverter', 'name': 'Module PV3', 'dep': {'reg': Register.NO_INPUTS, 'gte': 3}}, # noqa: E501
|
'input_pv3': {'via': 'inverter', 'name': 'Module PV3', 'mdl': Register.PV3_MODEL, 'mf': Register.PV3_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 3}}, # noqa: E501
|
||||||
'input_pv4': {'via': 'inverter', 'name': 'Module PV4', 'dep': {'reg': Register.NO_INPUTS, 'gte': 4}}, # noqa: E501
|
'input_pv4': {'via': 'inverter', 'name': 'Module PV4', 'mdl': Register.PV4_MODEL, 'mf': Register.PV4_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 4}}, # noqa: E501
|
||||||
|
'input_pv5': {'via': 'inverter', 'name': 'Module PV5', 'mdl': Register.PV5_MODEL, 'mf': Register.PV5_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 5}}, # noqa: E501
|
||||||
|
'input_pv6': {'via': 'inverter', 'name': 'Module PV6', 'mdl': Register.PV6_MODEL, 'mf': Register.PV6_MANUFACTURER, 'dep': {'reg': Register.NO_INPUTS, 'gte': 6}}, # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
__comm_type_val_tpl = "{%set com_types = ['n/a','Wi-Fi', 'G4', 'G5', 'GPRS'] %}{{com_types[value_json['Communication_Type']|int(0)]|default(value_json['Communication_Type'])}}" # noqa: E501
|
__comm_type_val_tpl = "{%set com_types = ['n/a','Wi-Fi', 'G4', 'G5', 'GPRS'] %}{{com_types[value_json['Communication_Type']|int(0)]|default(value_json['Communication_Type'])}}" # noqa: E501
|
||||||
@@ -151,6 +193,19 @@ class Infos:
|
|||||||
Register.MAX_DESIGNED_POWER: {'name': ['inverter', 'Max_Designed_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'designed_power_', 'fmt': '| string + " W"', 'name': 'Max Designed Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.MAX_DESIGNED_POWER: {'name': ['inverter', 'Max_Designed_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'designed_power_', 'fmt': '| string + " W"', 'name': 'Max Designed Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.RATED_POWER: {'name': ['inverter', 'Rated_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'rated_power_', 'fmt': '| string + " W"', 'name': 'Rated Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.RATED_POWER: {'name': ['inverter', 'Rated_Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'inverter', 'dev_cla': None, 'stat_cla': None, 'id': 'rated_power_', 'fmt': '| string + " W"', 'name': 'Rated Power', 'icon': 'mdi:lightning-bolt', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
|
||||||
|
Register.PV1_MANUFACTURER: {'name': ['inverter', 'PV1_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV1_MODEL: {'name': ['inverter', 'PV1_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV2_MANUFACTURER: {'name': ['inverter', 'PV2_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV2_MODEL: {'name': ['inverter', 'PV2_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV3_MANUFACTURER: {'name': ['inverter', 'PV3_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV3_MODEL: {'name': ['inverter', 'PV3_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV4_MANUFACTURER: {'name': ['inverter', 'PV4_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV4_MODEL: {'name': ['inverter', 'PV4_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV5_MANUFACTURER: {'name': ['inverter', 'PV5_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV5_MODEL: {'name': ['inverter', 'PV5_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV6_MANUFACTURER: {'name': ['inverter', 'PV6_Manufacturer'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
Register.PV6_MODEL: {'name': ['inverter', 'PV6_Model'], 'level': logging.DEBUG, 'unit': ''}, # noqa: E501
|
||||||
|
|
||||||
# proxy:
|
# proxy:
|
||||||
Register.INVERTER_CNT: {'name': ['proxy', 'Inverter_Cnt'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_count_', 'fmt': '| int', 'name': 'Active Inverter Connections', 'icon': 'mdi:counter'}}, # noqa: E501
|
Register.INVERTER_CNT: {'name': ['proxy', 'Inverter_Cnt'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'inv_count_', 'fmt': '| int', 'name': 'Active Inverter Connections', 'icon': 'mdi:counter'}}, # noqa: E501
|
||||||
Register.UNKNOWN_SNR: {'name': ['proxy', 'Unknown_SNR'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'unknown_snr_', 'fmt': '| int', 'name': 'Unknown Serial No', 'icon': 'mdi:counter', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.UNKNOWN_SNR: {'name': ['proxy', 'Unknown_SNR'], 'singleton': True, 'ha': {'dev': 'proxy', 'comp': 'sensor', 'dev_cla': None, 'stat_cla': None, 'id': 'unknown_snr_', 'fmt': '| int', 'name': 'Unknown Serial No', 'icon': 'mdi:counter', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
@@ -193,16 +248,22 @@ class Infos:
|
|||||||
# input measures:
|
# input measures:
|
||||||
Register.PV1_VOLTAGE: {'name': ['input', 'pv1', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv1', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv1_', 'val_tpl': "{{ (value_json['pv1']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV1_VOLTAGE: {'name': ['input', 'pv1', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv1', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv1_', 'val_tpl': "{{ (value_json['pv1']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV1_CURRENT: {'name': ['input', 'pv1', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv1', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv1_', 'val_tpl': "{{ (value_json['pv1']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV1_CURRENT: {'name': ['input', 'pv1', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv1', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv1_', 'val_tpl': "{{ (value_json['pv1']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV1_POWER: {'name': ['input', 'pv1', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv1', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv1_', 'val_tpl': "{{ (value_json['pv1']['Power'] | float)}}"}}, # noqa: E501
|
Register.PV1_POWER: {'name': ['input', 'pv1', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv1', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv1_', 'val_tpl': "{{ (value_json['pv1']['Power'] | float)}}"}}, # noqa: E501
|
||||||
Register.PV2_VOLTAGE: {'name': ['input', 'pv2', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv2', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv2_', 'val_tpl': "{{ (value_json['pv2']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV2_VOLTAGE: {'name': ['input', 'pv2', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv2', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv2_', 'val_tpl': "{{ (value_json['pv2']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV2_CURRENT: {'name': ['input', 'pv2', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv2', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv2_', 'val_tpl': "{{ (value_json['pv2']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV2_CURRENT: {'name': ['input', 'pv2', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv2', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv2_', 'val_tpl': "{{ (value_json['pv2']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV2_POWER: {'name': ['input', 'pv2', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv2', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv2_', 'val_tpl': "{{ (value_json['pv2']['Power'] | float)}}"}}, # noqa: E501
|
Register.PV2_POWER: {'name': ['input', 'pv2', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv2', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv2_', 'val_tpl': "{{ (value_json['pv2']['Power'] | float)}}"}}, # noqa: E501
|
||||||
Register.PV3_VOLTAGE: {'name': ['input', 'pv3', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv3', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv3_', 'val_tpl': "{{ (value_json['pv3']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV3_VOLTAGE: {'name': ['input', 'pv3', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv3', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv3_', 'val_tpl': "{{ (value_json['pv3']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV3_CURRENT: {'name': ['input', 'pv3', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv3', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv3_', 'val_tpl': "{{ (value_json['pv3']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV3_CURRENT: {'name': ['input', 'pv3', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv3', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv3_', 'val_tpl': "{{ (value_json['pv3']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV3_POWER: {'name': ['input', 'pv3', 'Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'input_pv3', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv3_', 'val_tpl': "{{ (value_json['pv3']['Power'] | float)}}"}}, # noqa: E501
|
Register.PV3_POWER: {'name': ['input', 'pv3', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv3', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv3_', 'val_tpl': "{{ (value_json['pv3']['Power'] | float)}}"}}, # noqa: E501
|
||||||
Register.PV4_VOLTAGE: {'name': ['input', 'pv4', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv4', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv4_', 'val_tpl': "{{ (value_json['pv4']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV4_VOLTAGE: {'name': ['input', 'pv4', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv4', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv4_', 'val_tpl': "{{ (value_json['pv4']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV4_CURRENT: {'name': ['input', 'pv4', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv4', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv4_', 'val_tpl': "{{ (value_json['pv4']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
Register.PV4_CURRENT: {'name': ['input', 'pv4', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv4', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv4_', 'val_tpl': "{{ (value_json['pv4']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
Register.PV4_POWER: {'name': ['input', 'pv4', 'Power'], 'level': logging.DEBUG, 'unit': 'W', 'ha': {'dev': 'input_pv4', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv4_', 'val_tpl': "{{ (value_json['pv4']['Power'] | float)}}"}}, # noqa: E501
|
Register.PV4_POWER: {'name': ['input', 'pv4', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv4', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv4_', 'val_tpl': "{{ (value_json['pv4']['Power'] | float)}}"}}, # noqa: E501
|
||||||
|
Register.PV5_VOLTAGE: {'name': ['input', 'pv5', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv5', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv5_', 'val_tpl': "{{ (value_json['pv5']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.PV5_CURRENT: {'name': ['input', 'pv5', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv5', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv5_', 'val_tpl': "{{ (value_json['pv5']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.PV5_POWER: {'name': ['input', 'pv5', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv5', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv5_', 'val_tpl': "{{ (value_json['pv5']['Power'] | float)}}"}}, # noqa: E501
|
||||||
|
Register.PV6_VOLTAGE: {'name': ['input', 'pv6', 'Voltage'], 'level': logging.DEBUG, 'unit': 'V', 'ha': {'dev': 'input_pv6', 'dev_cla': 'voltage', 'stat_cla': 'measurement', 'id': 'volt_pv6_', 'val_tpl': "{{ (value_json['pv6']['Voltage'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.PV6_CURRENT: {'name': ['input', 'pv6', 'Current'], 'level': logging.DEBUG, 'unit': 'A', 'ha': {'dev': 'input_pv6', 'dev_cla': 'current', 'stat_cla': 'measurement', 'id': 'cur_pv6_', 'val_tpl': "{{ (value_json['pv6']['Current'] | float)}}", 'icon': 'mdi:gauge', 'ent_cat': 'diagnostic'}}, # noqa: E501
|
||||||
|
Register.PV6_POWER: {'name': ['input', 'pv6', 'Power'], 'level': logging.INFO, 'unit': 'W', 'ha': {'dev': 'input_pv6', 'dev_cla': 'power', 'stat_cla': 'measurement', 'id': 'power_pv6_', 'val_tpl': "{{ (value_json['pv6']['Power'] | float)}}"}}, # noqa: E501
|
||||||
Register.PV1_DAILY_GENERATION: {'name': ['input', 'pv1', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv1', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv1_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv1']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
Register.PV1_DAILY_GENERATION: {'name': ['input', 'pv1', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv1', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv1_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv1']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
Register.PV1_TOTAL_GENERATION: {'name': ['input', 'pv1', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv1', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv1_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv1']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
Register.PV1_TOTAL_GENERATION: {'name': ['input', 'pv1', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv1', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv1_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv1']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
Register.PV2_DAILY_GENERATION: {'name': ['input', 'pv2', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv2', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv2_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv2']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
Register.PV2_DAILY_GENERATION: {'name': ['input', 'pv2', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv2', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv2_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv2']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
@@ -211,6 +272,10 @@ class Infos:
|
|||||||
Register.PV3_TOTAL_GENERATION: {'name': ['input', 'pv3', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv3', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv3_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv3']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
Register.PV3_TOTAL_GENERATION: {'name': ['input', 'pv3', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv3', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv3_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv3']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
Register.PV4_DAILY_GENERATION: {'name': ['input', 'pv4', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv4', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv4_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv4']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
Register.PV4_DAILY_GENERATION: {'name': ['input', 'pv4', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv4', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv4_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv4']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
Register.PV4_TOTAL_GENERATION: {'name': ['input', 'pv4', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv4', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv4_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv4']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
Register.PV4_TOTAL_GENERATION: {'name': ['input', 'pv4', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv4', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv4_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv4']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
|
Register.PV5_DAILY_GENERATION: {'name': ['input', 'pv5', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv5', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv5_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv5']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
|
Register.PV5_TOTAL_GENERATION: {'name': ['input', 'pv5', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv5', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv5_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv5']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
|
Register.PV6_DAILY_GENERATION: {'name': ['input', 'pv6', 'Daily_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv6', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_pv6_', 'name': 'Daily Generation', 'val_tpl': "{{ (value_json['pv6']['Daily_Generation'] | float)}}", 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
|
Register.PV6_TOTAL_GENERATION: {'name': ['input', 'pv6', 'Total_Generation'], 'level': logging.DEBUG, 'unit': 'kWh', 'ha': {'dev': 'input_pv6', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_pv6_', 'name': 'Total Generation', 'val_tpl': "{{ (value_json['pv6']['Total_Generation'] | float)}}", 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
# total:
|
# total:
|
||||||
Register.DAILY_GENERATION: {'name': ['total', 'Daily_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_', 'fmt': '| float', 'name': 'Daily Generation', 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
Register.DAILY_GENERATION: {'name': ['total', 'Daily_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total_increasing', 'id': 'daily_gen_', 'fmt': '| float', 'name': 'Daily Generation', 'icon': 'mdi:solar-power-variant', 'must_incr': True}}, # noqa: E501
|
||||||
Register.TOTAL_GENERATION: {'name': ['total', 'Total_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_', 'fmt': '| float', 'name': 'Total Generation', 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
Register.TOTAL_GENERATION: {'name': ['total', 'Total_Generation'], 'level': logging.INFO, 'unit': 'kWh', 'ha': {'dev': 'inverter', 'dev_cla': 'energy', 'stat_cla': 'total', 'id': 'total_gen_', 'fmt': '| float', 'name': 'Total Generation', 'icon': 'mdi:solar-power', 'must_incr': True}}, # noqa: E501
|
||||||
@@ -233,24 +298,14 @@ class Infos:
|
|||||||
@property
|
@property
|
||||||
def info_defs(self) -> dict:
|
def info_defs(self) -> dict:
|
||||||
return self.__info_defs
|
return self.__info_defs
|
||||||
'''
|
|
||||||
if __name__ == "app.src.messages":
|
|
||||||
@info_defs.setter
|
|
||||||
def info_defs(self, value: dict) -> None:
|
|
||||||
self.__info_defs = value
|
|
||||||
|
|
||||||
@info_devs.setter
|
|
||||||
def info_devs(self, value: dict) -> None:
|
|
||||||
self.__info_devs = value
|
|
||||||
'''
|
|
||||||
|
|
||||||
def dev_value(self, idx: str | int) -> str | int | float | None:
|
def dev_value(self, idx: str | int) -> str | int | float | None:
|
||||||
'''returns the stored device value from our database
|
'''returns the stored device value from our database
|
||||||
|
|
||||||
idx:int ==> lookup the value in the database and return it as str,
|
idx:int ==> lookup the value in the database and return it as str,
|
||||||
int or flout. If the value is not available return 'None'
|
int or float. If the value is not available return 'None'
|
||||||
idx:str ==> returns the string as a fixed value without a
|
idx:str ==> returns the string as a fixed value without a
|
||||||
database loopup
|
database lookup
|
||||||
'''
|
'''
|
||||||
if type(idx) is str:
|
if type(idx) is str:
|
||||||
return idx # return idx as a fixed value
|
return idx # return idx as a fixed value
|
||||||
@@ -300,7 +355,8 @@ class Infos:
|
|||||||
if res:
|
if res:
|
||||||
yield res
|
yield res
|
||||||
|
|
||||||
def ha_conf(self, key, ha_prfx, node_id, snr, singleton: bool, sug_area: str = '') -> tuple[str, str, str, str]: # noqa: E501
|
def ha_conf(self, key, ha_prfx, node_id, snr, singleton: bool,
|
||||||
|
sug_area: str = '') -> tuple[str, str, str, str] | None:
|
||||||
if key not in self.info_defs:
|
if key not in self.info_defs:
|
||||||
return None
|
return None
|
||||||
row = self.info_defs[key]
|
row = self.info_defs[key]
|
||||||
@@ -404,7 +460,7 @@ class Infos:
|
|||||||
return json.dumps(attr), component, node_id, attr['uniq_id']
|
return json.dumps(attr), component, node_id, attr['uniq_id']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _key_obj(self, id) -> list:
|
def _key_obj(self, id: Register) -> list:
|
||||||
d = self.info_defs.get(id, {'name': None, 'level': logging.DEBUG,
|
d = self.info_defs.get(id, {'name': None, 'level': logging.DEBUG,
|
||||||
'unit': ''})
|
'unit': ''})
|
||||||
if 'ha' in d and 'must_incr' in d['ha']:
|
if 'ha' in d and 'must_incr' in d['ha']:
|
||||||
@@ -414,7 +470,7 @@ class Infos:
|
|||||||
|
|
||||||
return d['name'], d['level'], d['unit'], must_incr
|
return d['name'], d['level'], d['unit'], must_incr
|
||||||
|
|
||||||
def update_db(self, keys, must_incr, result):
|
def update_db(self, keys: list, must_incr: bool, result):
|
||||||
name = ''
|
name = ''
|
||||||
dict = self.db
|
dict = self.db
|
||||||
for key in keys[:-1]:
|
for key in keys[:-1]:
|
||||||
@@ -434,30 +490,46 @@ class Infos:
|
|||||||
name += keys[-1]
|
name += keys[-1]
|
||||||
return name, update
|
return name, update
|
||||||
|
|
||||||
def set_db_def_value(self, id, value):
|
def set_db_def_value(self, id: Register, value) -> None:
|
||||||
'''set default value'''
|
'''set default value'''
|
||||||
row = self.info_defs[id]
|
row = self.info_defs[id]
|
||||||
if isinstance(row, dict): # pragma: no cover
|
if isinstance(row, dict):
|
||||||
keys = row['name']
|
keys = row['name']
|
||||||
self.update_db(keys, False, value)
|
self.update_db(keys, False, value)
|
||||||
|
|
||||||
def get_db_value(self, id, not_found_result=None):
|
def reg_clr_at_midnight(self, prfx: str) -> None:
|
||||||
|
'''register all registers for the 'ClrAtMidnight' class and
|
||||||
|
check if device of every register is available otherwise ignore
|
||||||
|
the register.
|
||||||
|
|
||||||
|
prfx:str ==> prefix for the home assistant 'stat_t string''
|
||||||
|
'''
|
||||||
|
for id, row in self.info_defs.items():
|
||||||
|
if 'ha' in row:
|
||||||
|
ha = row['ha']
|
||||||
|
if 'dev' in ha:
|
||||||
|
device = self.info_devs[ha['dev']]
|
||||||
|
if 'dep' in device and self.ignore_this_device(device['dep']): # noqa: E501
|
||||||
|
continue
|
||||||
|
|
||||||
|
keys = row['name']
|
||||||
|
ClrAtMidnight.add(keys, prfx, id)
|
||||||
|
|
||||||
|
def get_db_value(self, id: Register, not_found_result: any = None):
|
||||||
'''get database value'''
|
'''get database value'''
|
||||||
row = self.info_defs[id]
|
row = self.info_defs[id]
|
||||||
if isinstance(row, dict): # pragma: no cover
|
if isinstance(row, dict):
|
||||||
keys = row['name']
|
keys = row['name']
|
||||||
elm = self.db
|
elm = self.db
|
||||||
for key in keys[:-1]:
|
for key in keys:
|
||||||
if key not in elm:
|
if key not in elm:
|
||||||
return not_found_result
|
return not_found_result
|
||||||
elm = elm[key]
|
elm = elm[key]
|
||||||
|
return elm
|
||||||
if keys[-1] in elm:
|
|
||||||
return elm[keys[-1]]
|
|
||||||
return not_found_result
|
return not_found_result
|
||||||
|
|
||||||
def ignore_this_device(self, dep: dict) -> bool:
|
def ignore_this_device(self, dep: dict) -> bool:
|
||||||
'''Checks the equation in the dep dict
|
'''Checks the equation in the dep(endency) dict
|
||||||
|
|
||||||
returns 'False' only if the equation is valid;
|
returns 'False' only if the equation is valid;
|
||||||
'True' in any other case'''
|
'True' in any other case'''
|
||||||
@@ -471,3 +543,20 @@ class Infos:
|
|||||||
elif 'less_eq' in dep:
|
elif 'less_eq' in dep:
|
||||||
return not value <= dep['less_eq']
|
return not value <= dep['less_eq']
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def set_pv_module_details(self, inv: dict) -> None:
|
||||||
|
map = {'pv1': {'manufacturer': Register.PV1_MANUFACTURER, 'model': Register.PV1_MODEL}, # noqa: E501
|
||||||
|
'pv2': {'manufacturer': Register.PV2_MANUFACTURER, 'model': Register.PV2_MODEL}, # noqa: E501
|
||||||
|
'pv3': {'manufacturer': Register.PV3_MANUFACTURER, 'model': Register.PV3_MODEL}, # noqa: E501
|
||||||
|
'pv4': {'manufacturer': Register.PV4_MANUFACTURER, 'model': Register.PV4_MODEL}, # noqa: E501
|
||||||
|
'pv5': {'manufacturer': Register.PV5_MANUFACTURER, 'model': Register.PV5_MODEL}, # noqa: E501
|
||||||
|
'pv6': {'manufacturer': Register.PV6_MANUFACTURER, 'model': Register.PV6_MODEL} # noqa: E501
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, reg in map.items():
|
||||||
|
if key in inv:
|
||||||
|
if 'manufacturer' in inv[key]:
|
||||||
|
self.set_db_def_value(reg['manufacturer'],
|
||||||
|
inv[key]['manufacturer'])
|
||||||
|
if 'type' in inv[key]:
|
||||||
|
self.set_db_def_value(reg['model'], inv[key]['type'])
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ class Message(metaclass=IterRegistry):
|
|||||||
# to our _recv_buffer
|
# to our _recv_buffer
|
||||||
return # pragma: no cover
|
return # pragma: no cover
|
||||||
|
|
||||||
|
def _update_header(self, _forward_buffer):
|
||||||
|
'''callback for updating the header of the forward buffer'''
|
||||||
|
return
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Our puplic methods
|
Our puplic methods
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Mqtt(metaclass=Singleton):
|
|||||||
def __init__(self, cb_MqttIsUp):
|
def __init__(self, cb_MqttIsUp):
|
||||||
logger_mqtt.debug('MQTT: __init__')
|
logger_mqtt.debug('MQTT: __init__')
|
||||||
if cb_MqttIsUp:
|
if cb_MqttIsUp:
|
||||||
self.cb_MqttIsUp = cb_MqttIsUp
|
self.__cb_MqttIsUp = cb_MqttIsUp
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
self.task = loop.create_task(self.__loop())
|
self.task = loop.create_task(self.__loop())
|
||||||
self.ha_restarts = 0
|
self.ha_restarts = 0
|
||||||
@@ -70,24 +70,32 @@ class Mqtt(metaclass=Singleton):
|
|||||||
async with self.__client:
|
async with self.__client:
|
||||||
logger_mqtt.info('MQTT broker connection established')
|
logger_mqtt.info('MQTT broker connection established')
|
||||||
|
|
||||||
if self.cb_MqttIsUp:
|
if self.__cb_MqttIsUp:
|
||||||
await self.cb_MqttIsUp()
|
await self.__cb_MqttIsUp()
|
||||||
|
|
||||||
async with self.__client.messages() as messages:
|
# async with self.__client.messages() as messages:
|
||||||
await self.__client.subscribe(
|
await self.__client.subscribe(
|
||||||
f"{ha['auto_conf_prefix']}"
|
f"{ha['auto_conf_prefix']}"
|
||||||
"/status")
|
"/status")
|
||||||
async for message in messages:
|
async for message in self.__client.messages:
|
||||||
status = message.payload.decode("UTF-8")
|
status = message.payload.decode("UTF-8")
|
||||||
logger_mqtt.info('Home-Assistant Status:'
|
logger_mqtt.info('Home-Assistant Status:'
|
||||||
f' {status}')
|
f' {status}')
|
||||||
if status == 'online':
|
if status == 'online':
|
||||||
self.ha_restarts += 1
|
self.ha_restarts += 1
|
||||||
await self.cb_MqttIsUp()
|
await self.__cb_MqttIsUp()
|
||||||
|
|
||||||
except aiomqtt.MqttError:
|
except aiomqtt.MqttError:
|
||||||
logger_mqtt.info(f"Connection lost; Reconnecting in {interval}"
|
if Config.is_default('mqtt'):
|
||||||
" seconds ...")
|
logger_mqtt.info(
|
||||||
|
"MQTT is unconfigured; Check your config.toml!")
|
||||||
|
interval = 30
|
||||||
|
else:
|
||||||
|
interval = 5 # Seconds
|
||||||
|
logger_mqtt.info(
|
||||||
|
f"Connection lost; Reconnecting in {interval}"
|
||||||
|
" seconds ...")
|
||||||
|
|
||||||
await asyncio.sleep(interval)
|
await asyncio.sleep(interval)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger_mqtt.debug("MQTT task cancelled")
|
logger_mqtt.debug("MQTT task cancelled")
|
||||||
|
|||||||
30
app/src/scheduler.py
Normal file
30
app/src/scheduler.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from mqtt import Mqtt
|
||||||
|
from aiocron import crontab
|
||||||
|
from infos import ClrAtMidnight
|
||||||
|
|
||||||
|
logger_mqtt = logging.getLogger('mqtt')
|
||||||
|
|
||||||
|
|
||||||
|
class Schedule:
|
||||||
|
mqtt = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def start(cls) -> None:
|
||||||
|
'''Start the scheduler and schedule the tasks (cron jobs)'''
|
||||||
|
logging.info("Scheduler init")
|
||||||
|
cls.mqtt = Mqtt(None)
|
||||||
|
|
||||||
|
crontab('0 0 * * *', func=cls.atmidnight, start=True)
|
||||||
|
# crontab('*/5 * * * *', func=cls.atmidnight, start=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def atmidnight(cls) -> None:
|
||||||
|
'''Clear daily counters at midnight'''
|
||||||
|
logging.info("Clear daily counters at midnight")
|
||||||
|
|
||||||
|
for key, data in ClrAtMidnight.elm():
|
||||||
|
logger_mqtt.debug(f'{key}: {data}')
|
||||||
|
data_json = json.dumps(data)
|
||||||
|
await cls.mqtt.publish(f"{key}", data_json)
|
||||||
@@ -8,6 +8,7 @@ from messages import Message
|
|||||||
from inverter import Inverter
|
from inverter import Inverter
|
||||||
from gen3.inverter_g3 import InverterG3
|
from gen3.inverter_g3 import InverterG3
|
||||||
from gen3plus.inverter_g3p import InverterG3P
|
from gen3plus.inverter_g3p import InverterG3P
|
||||||
|
from scheduler import Schedule
|
||||||
from config import Config
|
from config import Config
|
||||||
|
|
||||||
|
|
||||||
@@ -73,6 +74,7 @@ if __name__ == "__main__":
|
|||||||
logging.getLogger('msg').setLevel(log_level)
|
logging.getLogger('msg').setLevel(log_level)
|
||||||
logging.getLogger('conn').setLevel(log_level)
|
logging.getLogger('conn').setLevel(log_level)
|
||||||
logging.getLogger('data').setLevel(log_level)
|
logging.getLogger('data').setLevel(log_level)
|
||||||
|
# logging.getLogger('mqtt').setLevel(log_level)
|
||||||
|
|
||||||
# read config file
|
# read config file
|
||||||
Config.read()
|
Config.read()
|
||||||
@@ -81,6 +83,8 @@ if __name__ == "__main__":
|
|||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
Schedule.start()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Register some UNIX Signal handler for a gracefully server shutdown
|
# Register some UNIX Signal handler for a gracefully server shutdown
|
||||||
# on Docker restart and stop
|
# on Docker restart and stop
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# test_with_pytest.py
|
# test_with_pytest.py
|
||||||
import pytest, json
|
import pytest, json
|
||||||
from app.src.infos import Register
|
from app.src.infos import Register, ClrAtMidnight
|
||||||
from app.src.gen3.infos_g3 import InfosG3
|
from app.src.gen3.infos_g3 import InfosG3
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -524,3 +524,69 @@ def test_invalid_data_type(InvalidDataSeq):
|
|||||||
val = i.dev_value(Register.INVALID_DATA_TYPE) # check invalid data type counter
|
val = i.dev_value(Register.INVALID_DATA_TYPE) # check invalid data type counter
|
||||||
assert val == 1
|
assert val == 1
|
||||||
|
|
||||||
|
def test_clr_at_midnight():
|
||||||
|
i = InfosG3()
|
||||||
|
i.static_init() # initialize counter
|
||||||
|
i.set_db_def_value(Register.NO_INPUTS, 2)
|
||||||
|
val = i.dev_value(Register.NO_INPUTS) # valid addr but not initiliazed
|
||||||
|
assert val == 2
|
||||||
|
i.info_defs[Register.TEST_REG1] = { # add a entry with incomplete ha definition
|
||||||
|
'name': ['test', 'grp', 'REG_1'], 'ha': {'dev_cla': None }
|
||||||
|
}
|
||||||
|
i.reg_clr_at_midnight('tsun/inv_1/')
|
||||||
|
# tsun/inv_2/input
|
||||||
|
assert json.dumps(ClrAtMidnight.db['tsun/inv_1/total']) == json.dumps({'Daily_Generation': 0})
|
||||||
|
assert json.dumps(ClrAtMidnight.db['tsun/inv_1/input']) == json.dumps({"pv1": {"Daily_Generation": 0}, "pv2": {"Daily_Generation": 0}})
|
||||||
|
|
||||||
|
i.reg_clr_at_midnight('tsun/inv_1/')
|
||||||
|
assert json.dumps(ClrAtMidnight.db['tsun/inv_1/total']) == json.dumps({'Daily_Generation': 0})
|
||||||
|
assert json.dumps(ClrAtMidnight.db['tsun/inv_1/input']) == json.dumps({"pv1": {"Daily_Generation": 0}, "pv2": {"Daily_Generation": 0}})
|
||||||
|
|
||||||
|
test = 0
|
||||||
|
for key, data in ClrAtMidnight.elm():
|
||||||
|
if key == 'tsun/inv_1/total':
|
||||||
|
assert json.dumps(data) == json.dumps({'Daily_Generation': 0})
|
||||||
|
test += 1
|
||||||
|
elif key == 'tsun/inv_1/input':
|
||||||
|
assert json.dumps(data) == json.dumps({"pv1": {"Daily_Generation": 0}, "pv2": {"Daily_Generation": 0}})
|
||||||
|
test += 1
|
||||||
|
assert test == 2
|
||||||
|
assert json.dumps(ClrAtMidnight.db) == json.dumps({})
|
||||||
|
|
||||||
|
i.reg_clr_at_midnight('tsun/inv_1/')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_pv_module_config():
|
||||||
|
i = InfosG3()
|
||||||
|
# i.set_db_def_value(Register.NO_INPUTS, 2)
|
||||||
|
|
||||||
|
dt = {
|
||||||
|
'pv1':{'manufacturer':'TSUN1','type': 'Module 100W'},
|
||||||
|
'pv2':{'manufacturer':'TSUN2'},
|
||||||
|
'pv3':{'manufacturer':'TSUN3','type': 'Module 300W'},
|
||||||
|
'pv4':{'type': 'Module 400W'},
|
||||||
|
'pv5':{},
|
||||||
|
}
|
||||||
|
i.set_pv_module_details(dt)
|
||||||
|
assert 'TSUN1' == i.dev_value(Register.PV1_MANUFACTURER)
|
||||||
|
assert 'TSUN2' == i.dev_value(Register.PV2_MANUFACTURER)
|
||||||
|
assert 'TSUN3' == i.dev_value(Register.PV3_MANUFACTURER)
|
||||||
|
assert None == i.dev_value(Register.PV4_MANUFACTURER)
|
||||||
|
assert None == i.dev_value(Register.PV5_MANUFACTURER)
|
||||||
|
assert 'Module 100W' == i.dev_value(Register.PV1_MODEL)
|
||||||
|
assert None == i.dev_value(Register.PV2_MODEL)
|
||||||
|
assert 'Module 300W' == i.dev_value(Register.PV3_MODEL)
|
||||||
|
assert 'Module 400W' == i.dev_value(Register.PV4_MODEL)
|
||||||
|
assert None == i.dev_value(Register.PV5_MODEL)
|
||||||
|
|
||||||
|
def test_broken_info_defs():
|
||||||
|
i = InfosG3()
|
||||||
|
val = i.get_db_value(Register.NO_INPUTS, 666)
|
||||||
|
assert val == 666
|
||||||
|
i.info_defs[Register.TEST_REG1] = 'test' # add a string instead of a dict
|
||||||
|
val = i.get_db_value(Register.TEST_REG1, 666)
|
||||||
|
assert val == 666
|
||||||
|
i.set_db_def_value(Register.TEST_REG1, 2)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import pytest, json
|
import pytest
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||||
from app.src.config import Config
|
from app.src.config import Config
|
||||||
from app.src.infos import Infos, Register
|
from app.src.infos import Infos, Register
|
||||||
@@ -6,6 +9,9 @@ from app.src.infos import Infos, Register
|
|||||||
# initialize the proxy statistics
|
# initialize the proxy statistics
|
||||||
Infos.static_init()
|
Infos.static_init()
|
||||||
|
|
||||||
|
timestamp = int(time.time()) # 1712861197
|
||||||
|
heartbeat = 60
|
||||||
|
|
||||||
class MemoryStream(SolarmanV5):
|
class MemoryStream(SolarmanV5):
|
||||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||||
super().__init__(server_side)
|
super().__init__(server_side)
|
||||||
@@ -19,6 +25,12 @@ class MemoryStream(SolarmanV5):
|
|||||||
self.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
self.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
||||||
self.db.stat['proxy']['AT_Command'] = 0
|
self.db.stat['proxy']['AT_Command'] = 0
|
||||||
|
|
||||||
|
def _timestamp(self):
|
||||||
|
return timestamp
|
||||||
|
|
||||||
|
def _heartbeat(self) -> int:
|
||||||
|
return heartbeat
|
||||||
|
|
||||||
|
|
||||||
def append_msg(self, msg):
|
def append_msg(self, msg):
|
||||||
self.__msg += msg
|
self.__msg += msg
|
||||||
@@ -42,9 +54,6 @@ class MemoryStream(SolarmanV5):
|
|||||||
pass
|
pass
|
||||||
return copied_bytes
|
return copied_bytes
|
||||||
|
|
||||||
def _timestamp(self):
|
|
||||||
return 1700260990000
|
|
||||||
|
|
||||||
def _SolarmanV5__flush_recv_msg(self) -> None:
|
def _SolarmanV5__flush_recv_msg(self) -> None:
|
||||||
super()._SolarmanV5__flush_recv_msg()
|
super()._SolarmanV5__flush_recv_msg()
|
||||||
self.msg_count += 1
|
self.msg_count += 1
|
||||||
@@ -60,6 +69,16 @@ def get_inv_no() -> bytes:
|
|||||||
def get_invalid_sn():
|
def get_invalid_sn():
|
||||||
return b'R170000000000002'
|
return b'R170000000000002'
|
||||||
|
|
||||||
|
def total():
|
||||||
|
ts = timestamp
|
||||||
|
# convert int to little-endian bytes
|
||||||
|
return struct.pack('<L',ts)
|
||||||
|
|
||||||
|
def hb():
|
||||||
|
hb = heartbeat
|
||||||
|
# convert int to little-endian bytes
|
||||||
|
return struct.pack('<L',hb)
|
||||||
|
|
||||||
def correct_checksum(buf):
|
def correct_checksum(buf):
|
||||||
checksum = sum(buf[1:]) & 0xff
|
checksum = sum(buf[1:]) & 0xff
|
||||||
return checksum.to_bytes(length=1)
|
return checksum.to_bytes(length=1)
|
||||||
@@ -90,8 +109,9 @@ def DeviceIndMsg(): # 0x4110
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def DeviceRspMsg(): # 0x1110
|
def DeviceRspMsg(): # 0x1110
|
||||||
msg = b'\xa5\x0a\x00\x10\x11\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
msg = b'\xa5\x0a\x00\x10\x11\x01\x01' +get_sn() +b'\x02\x01'
|
||||||
msg += b'\x66\x78\x00\x00\x00'
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
msg += correct_checksum(msg)
|
msg += correct_checksum(msg)
|
||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
@@ -158,7 +178,7 @@ def InvalidChecksum(): # 0x4110
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def InverterIndMsg(): # 0x4210
|
def InverterIndMsg(): # 0x4210
|
||||||
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +get_sn() +b'\x01\xb0\x02\xbc\xc8'
|
msg = b'\xa5\x99\x01\x10\x42\x01\x02' +get_sn() +b'\x01\xb0\x02\xbc\xc8'
|
||||||
msg += b'\x24\x32\x6c\x1f\x00\x00\xa0\x47\xe4\x33\x01\x00\x03\x08\x00\x00'
|
msg += b'\x24\x32\x6c\x1f\x00\x00\xa0\x47\xe4\x33\x01\x00\x03\x08\x00\x00'
|
||||||
msg += b'\x59\x31\x37\x45\x37\x41\x30\x46\x30\x31\x30\x42\x30\x31\x33\x45'
|
msg += b'\x59\x31\x37\x45\x37\x41\x30\x46\x30\x31\x30\x42\x30\x31\x33\x45'
|
||||||
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
@@ -290,8 +310,9 @@ def InverterIndMsg2000(): # 0x4210 rated Power 2000W
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def InverterRspMsg(): # 0x1210
|
def InverterRspMsg(): # 0x1210
|
||||||
msg = b'\xa5\x0a\x00\x10\x12\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
msg = b'\xa5\x0a\x00\x10\x12\x02\02' +get_sn() +b'\x01\x01'
|
||||||
msg += b'\x66\x78\x00\x00\x00'
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
msg += correct_checksum(msg)
|
msg += correct_checksum(msg)
|
||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
@@ -314,16 +335,27 @@ def HeartbeatIndMsg(): # 0x4710
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def HeartbeatRspMsg(): # 0x1710
|
def HeartbeatRspMsg(): # 0x1710
|
||||||
msg = b'\xa5\x0a\x00\x10\x17\x10\x84' +get_sn() +b'\x00\x01\x22\x71\x09'
|
msg = b'\xa5\x0a\x00\x10\x17\x11\x84' +get_sn() +b'\x00\x01'
|
||||||
msg += b'\x66\x78\x00\x00\x00'
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
msg += correct_checksum(msg)
|
msg += correct_checksum(msg)
|
||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def AtCommandIndMsg(): # 0x4510
|
def AtCommandIndMsg(): # 0x4510
|
||||||
msg = b'\xa5\x01\x00\x10\x45\x10\x84' +get_sn()
|
msg = b'\xa5\x27\x00\x10\x45\x02\x01' +get_sn() +b'\x01\x02\x00'
|
||||||
msg += b'\x00'
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
msg += b'AT+TIME=214028,1,60,120\r'
|
||||||
|
msg += correct_checksum(msg)
|
||||||
|
msg += b'\x15'
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def AtCommandRspMsg(): # 0x1510
|
||||||
|
msg = b'\xa5\x0a\x00\x10\x15\x03\x01' +get_sn() +b'\x01\x01'
|
||||||
|
msg += total()
|
||||||
|
msg += hb()
|
||||||
msg += correct_checksum(msg)
|
msg += correct_checksum(msg)
|
||||||
msg += b'\x15'
|
msg += b'\x15'
|
||||||
return msg
|
return msg
|
||||||
@@ -349,7 +381,7 @@ def test_read_message(DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == None
|
assert m.unique_id == None
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -370,7 +402,7 @@ def test_invalid_start_byte(InvalidStartByte, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0
|
assert m.unique_id == 0
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -390,7 +422,7 @@ def test_invalid_stop_byte(InvalidStopByte):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0
|
assert m.unique_id == 0
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -410,7 +442,7 @@ def test_invalid_stop_byte2(InvalidStopByte, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0
|
assert m.unique_id == 0
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==DeviceIndMsg
|
assert m._recv_buffer==DeviceIndMsg
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -424,7 +456,7 @@ def test_invalid_stop_byte2(InvalidStopByte, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == None
|
assert m.unique_id == None
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -446,7 +478,7 @@ def test_invalid_stop_start_byte(InvalidStopByte, InvalidStartByte):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0
|
assert m.unique_id == 0
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -466,7 +498,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0
|
assert m.unique_id == 0
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==DeviceIndMsg
|
assert m._recv_buffer==DeviceIndMsg
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -480,7 +512,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == None
|
assert m.unique_id == None
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -488,7 +520,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
|||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg, DeviceRspMsg):
|
||||||
ConfigNoTsunInv1
|
ConfigNoTsunInv1
|
||||||
m = MemoryStream(DeviceIndMsg, (0,))
|
m = MemoryStream(DeviceIndMsg, (0,))
|
||||||
m.append_msg(DeviceIndMsg)
|
m.append_msg(DeviceIndMsg)
|
||||||
@@ -499,10 +531,13 @@ def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:01'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
|
assert m._send_buffer==DeviceRspMsg
|
||||||
assert m._forward_buffer==b''
|
assert m._forward_buffer==b''
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
|
|
||||||
|
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||||
m.read() # read complete msg, and dispatch msg
|
m.read() # read complete msg, and dispatch msg
|
||||||
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
||||||
assert m.msg_count == 2
|
assert m.msg_count == 2
|
||||||
@@ -510,8 +545,9 @@ def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:01'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
|
assert m._send_buffer==DeviceRspMsg
|
||||||
assert m._forward_buffer==b''
|
assert m._forward_buffer==b''
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
@@ -528,7 +564,7 @@ def test_read_message_in_chunks(DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == 0 # should be None ?
|
assert m.unique_id == 0 # should be None ?
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:00'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.read() # read rest of message
|
m.read() # read rest of message
|
||||||
@@ -552,7 +588,7 @@ def test_read_message_in_chunks2(ConfigTsunInv1, DeviceIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:01'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m.msg_count == 1
|
assert m.msg_count == 1
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
@@ -563,7 +599,7 @@ def test_read_message_in_chunks2(ConfigTsunInv1, DeviceIndMsg):
|
|||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, InverterIndMsg, InverterRspMsg):
|
||||||
ConfigTsunAllowAll
|
ConfigTsunAllowAll
|
||||||
m = MemoryStream(DeviceIndMsg, (0,))
|
m = MemoryStream(DeviceIndMsg, (0,))
|
||||||
m.append_msg(InverterIndMsg)
|
m.append_msg(InverterIndMsg)
|
||||||
@@ -574,13 +610,12 @@ def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4110
|
assert m.control == 0x4110
|
||||||
assert m.serial == 0x0100
|
assert str(m.seq) == '01:01'
|
||||||
assert m.data_len == 0xd4
|
assert m.data_len == 0xd4
|
||||||
assert m.msg_count == 1
|
assert m.msg_count == 1
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
assert m._forward_buffer==DeviceIndMsg
|
assert m._forward_buffer==DeviceIndMsg
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==DeviceRspMsg
|
||||||
# assert m._send_buffer==MsgContactResp
|
|
||||||
|
|
||||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||||
m._init_new_client_conn()
|
m._init_new_client_conn()
|
||||||
@@ -597,11 +632,11 @@ def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4210
|
assert m.control == 0x4210
|
||||||
assert m.serial == 0x9ee6
|
assert str(m.seq) == '02:02'
|
||||||
assert m.data_len == 0x199
|
assert m.data_len == 0x199
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
assert m._forward_buffer==InverterIndMsg
|
assert m._forward_buffer==InverterIndMsg
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==InverterRspMsg
|
||||||
|
|
||||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||||
m._init_new_client_conn()
|
m._init_new_client_conn()
|
||||||
@@ -618,7 +653,7 @@ def test_unkown_message(ConfigTsunInv1, UnknownMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x5110
|
assert m.control == 0x5110
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '84:10'
|
||||||
assert m.data_len == 0x0a
|
assert m.data_len == 0x0a
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
@@ -636,11 +671,11 @@ def test_device_rsp(ConfigTsunInv1, DeviceRspMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x1110
|
assert m.control == 0x1110
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '01:01'
|
||||||
assert m.data_len == 0x0a
|
assert m.data_len == 0x0a
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
assert m._forward_buffer==DeviceRspMsg
|
assert m._forward_buffer==b'' # DeviceRspMsg
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
@@ -654,15 +689,15 @@ def test_inverter_rsp(ConfigTsunInv1, InverterRspMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x1210
|
assert m.control == 0x1210
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '02:02'
|
||||||
assert m.data_len == 0x0a
|
assert m.data_len == 0x0a
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
assert m._forward_buffer==InverterRspMsg
|
assert m._forward_buffer==b'' # InverterRspMsg
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg):
|
def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg, HeartbeatRspMsg):
|
||||||
ConfigTsunInv1
|
ConfigTsunInv1
|
||||||
m = MemoryStream(HeartbeatIndMsg, (0,))
|
m = MemoryStream(HeartbeatIndMsg, (0,))
|
||||||
m.read() # read complete msg, and dispatch msg
|
m.read() # read complete msg, and dispatch msg
|
||||||
@@ -672,10 +707,10 @@ def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
# assert m.unique_id == '2070233889'
|
# assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4710
|
assert m.control == 0x4710
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '84:11' # value after sending response
|
||||||
assert m.data_len == 0x01
|
assert m.data_len == 0x01
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==HeartbeatRspMsg
|
||||||
assert m._forward_buffer==HeartbeatIndMsg
|
assert m._forward_buffer==HeartbeatIndMsg
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
@@ -690,15 +725,15 @@ def test_heartbeat_rsp(ConfigTsunInv1, HeartbeatRspMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
assert m.unique_id == '2070233889'
|
assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x1710
|
assert m.control == 0x1710
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '11:84' # value after sending response
|
||||||
assert m.data_len == 0x0a
|
assert m.data_len == 0x0a
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==b''
|
||||||
assert m._forward_buffer==HeartbeatRspMsg
|
assert m._forward_buffer==b'' # HeartbeatRspMsg
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
def test_at_command_ind(ConfigTsunInv1, AtCommandIndMsg):
|
def test_at_command_ind(ConfigTsunInv1, AtCommandIndMsg, AtCommandRspMsg):
|
||||||
ConfigTsunInv1
|
ConfigTsunInv1
|
||||||
m = MemoryStream(AtCommandIndMsg, (0,))
|
m = MemoryStream(AtCommandIndMsg, (0,))
|
||||||
m.read() # read complete msg, and dispatch msg
|
m.read() # read complete msg, and dispatch msg
|
||||||
@@ -708,10 +743,10 @@ def test_at_command_ind(ConfigTsunInv1, AtCommandIndMsg):
|
|||||||
assert m.snr == 2070233889
|
assert m.snr == 2070233889
|
||||||
# assert m.unique_id == '2070233889'
|
# assert m.unique_id == '2070233889'
|
||||||
assert m.control == 0x4510
|
assert m.control == 0x4510
|
||||||
assert m.serial == 0x8410
|
assert str(m.seq) == '01:03'
|
||||||
assert m.data_len == 0x01
|
assert m.data_len == 39
|
||||||
assert m._recv_buffer==b''
|
assert m._recv_buffer==b''
|
||||||
assert m._send_buffer==b''
|
assert m._send_buffer==AtCommandRspMsg
|
||||||
assert m._forward_buffer==AtCommandIndMsg
|
assert m._forward_buffer==AtCommandIndMsg
|
||||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||||
assert m.db.stat['proxy']['AT_Command'] == 1
|
assert m.db.stat['proxy']['AT_Command'] == 1
|
||||||
@@ -779,3 +814,18 @@ def test_build_logger_modell(ConfigTsunAllowAll, DeviceIndMsg):
|
|||||||
assert 'LSW5BLE_17_02B0_1.05' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00')
|
assert 'LSW5BLE_17_02B0_1.05' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00')
|
||||||
assert 'LSW5BLE' == m.db.get_db_value(Register.CHIP_MODEL, 0)
|
assert 'LSW5BLE' == m.db.get_db_value(Register.CHIP_MODEL, 0)
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
|
def test_AT_cmd(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, AtCommandIndMsg):
|
||||||
|
ConfigTsunAllowAll
|
||||||
|
m = MemoryStream(DeviceIndMsg)
|
||||||
|
m.read()
|
||||||
|
assert m._recv_buffer==b''
|
||||||
|
assert m._send_buffer==DeviceRspMsg
|
||||||
|
assert m._forward_buffer==DeviceIndMsg
|
||||||
|
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||||
|
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||||
|
m.send_at_cmd('AT+TIME=214028,1,60,120')
|
||||||
|
assert m._recv_buffer==b''
|
||||||
|
assert m._send_buffer==AtCommandIndMsg
|
||||||
|
assert m._forward_buffer==b''
|
||||||
|
m.close()
|
||||||
|
|||||||
Reference in New Issue
Block a user