Compare commits

..

43 Commits

Author SHA1 Message Date
renovate[bot]
5d0208d4cc Update dependency python-dotenv to v1.2.1 2025-10-26 18:42:23 +00:00
Stefan Allius
997245429e Revert "Update gihub action to python 3.14 (#496)" (#498)
* revert to python 3.13 for github actions
2025-10-09 21:48:29 +02:00
renovate[bot]
d8a04fedb8 Update ghcr.io/hassio-addons/base Docker tag to v18.1.4 (#496)
* Update ghcr.io/hassio-addons/base Docker tag to v18.1.1

* Update ghcr.io/hassio-addons/base Docker tag to v18.1.4

* update changelog

* update action step name

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-10-09 21:16:14 +02:00
renovate[bot]
d83d6b2caa Update python Docker tag (#497)
* Update python Docker tag

* bump to py version 3.14

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-10-09 19:38:50 +02:00
renovate[bot]
9e9451b5e8 Update SonarSource/sonarqube-scan-action action to v6 (#493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-26 20:41:59 +02:00
renovate[bot]
f9df7a1dad Update dependency coverage to v7.10.7 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-26 20:39:50 +02:00
renovate[bot]
f9cadf0f1d Update ghcr.io/hassio-addons/base Docker tag to v18.1.2 (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-26 20:38:31 +02:00
renovate[bot]
783c1fd31e Update dependency pytest-cov to v7 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 22:57:58 +02:00
renovate[bot]
471c4412e5 Update actions/setup-python action to v6 (#485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 22:52:08 +02:00
renovate[bot]
bf27f40375 Update actions/checkout action to v5 (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 22:51:42 +02:00
renovate[bot]
2019037ab0 Update dependency pytest-asyncio to v1.2.0 (#492)
* Update dependency pytest-asyncio to v1.2.0

* don't stop the event loop between test

set the loop_scope to session for async tests

* remove loop_scope="session"

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-09-15 22:50:47 +02:00
renovate[bot]
45ab95a6b3 Update dependency pytest-cov to v6.3.0 (#488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 00:10:44 +02:00
renovate[bot]
94cdd977c7 Update dependency pytest to v8.4.2 (#486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 00:10:24 +02:00
renovate[bot]
35e1fe55e4 Update python Docker tag to v3.13.7 (#480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 00:07:56 +02:00
renovate[bot]
1642c157bb Update ghcr.io/hassio-addons/base Docker tag to v18.1.1 (#479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 00:07:11 +02:00
renovate[bot]
7bfac77546 Update dependency coverage to v7.10.6 (#477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 23:46:28 +02:00
renovate[bot]
e126f4e780 Update dependency pytest-asyncio to v1.1.0 (#476)
* Update dependency pytest-asyncio to v1.1.0

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-07-16 20:36:52 +02:00
Stefan Allius
7da7d6f15c Save task references (#475)
* Save a tast reference

Important: Save a reference of the created task,
to avoid a task disappearing mid-execution. The
event loop only keeps weak references to tasks.
A task that isn’t referenced elsewhere may get
garbage collected at any time, even before it’s
done. For reliable “fire-and-forget” background
tasks, gather them in a collection
2025-07-16 20:15:21 +02:00
Stefan Allius
8c3f3ba827 S allius/issue472 (#473)
* catch socket.gaierror exception

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 21:09:29 +02:00
renovate[bot]
0b05f6cd9a Update dependency coverage to v7.9.2 (#470)
* Update dependency coverage to v7.9.2

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-07-15 20:23:01 +02:00
renovate[bot]
0e35a506e0 Update ghcr.io/hassio-addons/base Docker tag to v18.0.3 (#469)
* update python and pip to compatible versions

* Update ghcr.io/hassio-addons/base Docker tag to v18.0.3

* add-on: remove armhf and armv7 support

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-07-15 20:13:55 +02:00
renovate[bot]
eba2c3e452 Update ghcr.io/hassio-addons/base Docker tag to v18 (#468)
* Update ghcr.io/hassio-addons/base Docker tag to v18

* improve docker annotations

* update python and pip to compatible versions

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-06-29 21:47:37 +02:00
renovate[bot]
118fab8b6c Update dependency python-dotenv to v1.1.1 (#467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 18:24:28 +02:00
Stefan Allius
d25f142e10 add links to add-on urls (#466)
* add links to add-on urls

* Add translations

* set app.testing to get exceptions during test

* improve unit-tests for the web-UI

* update changelog

* extend languages tests

* workaround for github runner
2025-06-22 21:39:31 +02:00
Stefan Allius
eb59e19c0a Fix Sonar Qube errors and warnings (#464)
* replace constructor call with a literal

  https://sonarcloud.io/project/issues?open=AZeMhhlEyR1Wrs09sNyb&id=s-allius_tsun-gen3-proxy

* re-raise cancel error after cleanup

https://sonarcloud.io/project/issues?open=AZeMhhltyR1Wrs09sNyc&id=s-allius_tsun-gen3-proxy

* remove duplicated line

* change send_modbus_cmd into a synchronous function

* make send_start_cmd synchronous

https://sonarcloud.io/project/issues?open=AZeMhhhyyR1Wrs09sNya&id=s-allius_tsun-gen3-proxy

* make more functions synchronous

* update changelog
2025-06-21 12:18:48 +02:00
Stefan Allius
bacebbd649 S allius/issue456 (#462)
* - remove unused 32-bit architectures from the prebuild multiarch containers

* update po file
2025-06-21 10:41:47 +02:00
renovate[bot]
ebbb675e63 Update dependency flake8 to v7.3.0 (#459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-21 10:27:45 +02:00
Stefan Allius
04fd9ed7f6 S allius/issue460 (#461)
* - Improve Makefile

* - Babel don't build new po file if only the pot creation-date was changed
2025-06-21 10:26:17 +02:00
renovate[bot]
f3c22c9853 Update dependency pytest to v8.4.1 (#458)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-20 10:51:02 +02:00
renovate[bot]
460db31fa6 Update python Docker tag to v3.13.5 (#453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-20 10:46:35 +02:00
renovate[bot]
144c9080cb Update dependency coverage to v7.9.1 (#454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 22:43:00 +02:00
renovate[bot]
dc1a28260e Update dependency coverage to v7.9.0 (#450)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 22:16:56 +02:00
renovate[bot]
e59529adc0 Update dependency pytest-cov to v6.2.1 (#449)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 22:13:41 +02:00
renovate[bot]
8d93b2a636 Update python Docker tag to v3.13.4 (#446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 22:12:01 +02:00
renovate[bot]
01e9e70957 Update dependency pytest to v8.4.0 (#444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 22:11:37 +02:00
renovate[bot]
1721bbebe2 Update dependency pytest-asyncio to v1 (#433)
* Update dependency pytest-asyncio to v1

* set version to 0.15.0

* Update dependency pytest-asyncio to v1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Stefan Allius <stefan.allius@t-online.de>
2025-05-31 23:55:50 +02:00
Stefan Allius
41168fbb4d S allius/issue438 (#442)
* Update change log (#436)

* S allius/issue427 (#434)

* mock the aiomqtt library and increse coverage

* test inv response for a mb scan request

* improve test coverage

* S allius/issue427 (#435)

* mock the aiomqtt library and increse coverage

* test inv response for a mb scan request

* improve test coverage

* improve test case

* version 0.14.0

* handle missing MQTT addon

- we have to check if the supervisor API and a
MQTT broker add-on is installed. If not we assume
the user has an external MQTT broker

* handle missing MQTT addon

* run also on releases/* branch

* avoid printing of the MQTT config inkl. password

* revise the log outputs

* update version 0.14.1

* new version 0.14.1
2025-05-31 23:30:16 +02:00
Stefan Allius
25ba6ef8f3 version 0.14.0 (#441) 2025-05-31 23:27:49 +02:00
Stefan Allius
2a40bd7b71 S allius/issue427 (#435)
* mock the aiomqtt library and increse coverage

* test inv response for a mb scan request

* improve test coverage

* improve test case
2025-05-26 23:42:13 +02:00
Stefan Allius
95182d2196 S allius/issue427 (#434)
* mock the aiomqtt library and increse coverage

* test inv response for a mb scan request

* improve test coverage
2025-05-26 23:16:33 +02:00
Stefan Allius
f1da544c88 S allius/update python (#431)
* S allius/update python (#430)

* add-on: bump python to version 3.12.10-r1 (#429)
2025-05-25 03:52:59 +02:00
Stefan Allius
7365980c2f S allius/update python (#430)
* add-on: bump python to version 3.12.10-r1 (#429)
2025-05-25 03:21:21 +02:00
Stefan Allius
11e3226460 add-on: bump python to version 3.12.10-r1 (#429) 2025-05-25 02:27:22 +02:00
40 changed files with 643 additions and 342 deletions

View File

@@ -5,7 +5,7 @@ name: Python application
on: on:
push: push:
branches: [ "main", "dev-*", "*/issue*" ] branches: [ "main", "dev-*", "*/issue*", "releases/*" ]
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
@@ -18,7 +18,7 @@ on:
- '**.dockerfile' # Do no build on *.dockerfile changes - '**.dockerfile' # Do no build on *.dockerfile changes
- '**.sh' # Do no build on *.sh changes - '**.sh' # Do no build on *.sh changes
pull_request: pull_request:
branches: [ "main", "dev-*" ] branches: [ "main", "dev-*", "releases/*" ]
permissions: permissions:
contents: read contents: read
@@ -34,11 +34,11 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up Python 3.13 - name: Set up Python 3.13
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.13" python-version: "3.13"
- name: Install dependencies - name: Install dependencies
@@ -58,7 +58,7 @@ jobs:
coverage report coverage report
- name: Analyze with SonarCloud - name: Analyze with SonarCloud
if: ${{ env.SONAR_TOKEN != 0 }} if: ${{ env.SONAR_TOKEN != 0 }}
uses: SonarSource/sonarqube-scan-action@v5 uses: SonarSource/sonarqube-scan-action@v6
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:

View File

@@ -1 +1 @@
3.13.2 3.14.0

View File

@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased] ## [unreleased]
- Update ghcr.io/hassio-addons/base Docker tag to v18.1.4
- Update dependency pytest-asyncio to v1.1.0
- save task references, to avoid a task disappearing mid-execution
- catch socket.gaierror exception and log this with info level
- Update dependency coverage to v7.9.2
- add-on: bump base-image to version 18.0.3
- add-on: remove armhf and armv7 support
- add-on: add links to config and log-file to the web-UI
- fix some SonarQube warnings
- remove unused 32-bit architectures
- Babel don't build new po file if only the pot creation-date was changed
- Improve Makefile
- Update dependency pytest-asyncio to v1
## [0.14.1] - 2025-05-31
- handle missing MQTT addon [#438](https://github.com/s-allius/tsun-gen3-proxy/issues/438)
## [0.14.0] - 2025-05-29
- add-on: bump python to version 3.12.10-r1 - add-on: bump python to version 3.12.10-r1
- set no of pv modules for MS800 GEN3PLUS inverters - set no of pv modules for MS800 GEN3PLUS inverters
- fix the paths to copy the config.example.toml file during proxy start - fix the paths to copy the config.example.toml file during proxy start

View File

@@ -1,27 +1,37 @@
.PHONY: build babel clean addon-dev addon-debug addon-rc addon-rel debug dev preview rc rel check-docker-compose install .PHONY: help build babel clean addon-dev addon-debug addon-rc addon-rel debug dev preview rc rel check-docker-compose install
babel: help: ## show help message
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[$$()% a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
babel: ## build language files
$(MAKE) -C app $@ $(MAKE) -C app $@
build: build:
$(MAKE) -C ha_addons $@ $(MAKE) -C ha_addons $@
clean: clean: ## delete all built files
$(MAKE) -C app $@ $(MAKE) -C app $@
$(MAKE) -C ha_addons $@ $(MAKE) -C ha_addons $@
debug dev preview rc rel: debug dev preview rc rel: ## build docker container in <dev|debg|rc|rel> version
$(MAKE) -C app babel $(MAKE) -C app babel
$(MAKE) -C app $@ $(MAKE) -C app $@
addon-dev addon-debug addon-rc addon-rel: addon-dev addon-debug addon-rc addon-rel: ## build HA add-on in <dev|debg|rc|rel> version
$(MAKE) -C app babel $(MAKE) -C app babel
$(MAKE) -C ha_addons $(patsubst addon-%,%,$@) $(MAKE) -C ha_addons $(patsubst addon-%,%,$@)
check-docker-compose: check-docker-compose: ## check the docker-compose file
docker-compose config -q docker-compose config -q
install: PY_VER := $(shell cat .python-version)
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt install: ## install requirements into the pyenv and switch to proper venv
python3 -m pip install -r requirements-test.txt @pyenv local $(PY_VER) || { pyenv install $(PY_VER) && pyenv local $(PY_VER) || exit 1; }
@pyenv exec pip install --upgrade pip
@pyenv exec pip install -r requirements.txt
@pyenv exec pip install -r requirements-test.txt
pyenv exec python --version
run: ## run proxy locally out of the actual venv
pyenv exec python app/src/server.py -c /app/src/cnf

View File

@@ -7,7 +7,7 @@
<p align="center">integration</p> <p align="center">integration</p>
<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-3130/"><img alt="Supported Python versions" src="https://img.shields.io/badge/python-3.13-blue.svg"></a> <a href="https://www.python.org/downloads/release/python-3140/"><img alt="Supported Python versions" src="https://img.shields.io/badge/python-3.14-blue.svg"></a>
<a href="https://aiomqtt.bo3hm.com/introduction.html"><img alt="Supported aiomqtt versions" src="https://img.shields.io/badge/aiomqtt-2.3.1-lightblue.svg"></a> <a href="https://aiomqtt.bo3hm.com/introduction.html"><img alt="Supported aiomqtt versions" src="https://img.shields.io/badge/aiomqtt-2.3.1-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://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>

View File

@@ -1 +1 @@
0.14.0 0.15.0

View File

@@ -4,7 +4,7 @@ ARG GID=1000
# #
# first stage for our base image # first stage for our base image
FROM python:3.13-alpine AS base FROM python:3.14-alpine AS base
COPY --chmod=0700 ./hardening_base.sh / COPY --chmod=0700 ./hardening_base.sh /
RUN apk upgrade --no-cache && \ RUN apk upgrade --no-cache && \

View File

@@ -55,7 +55,7 @@ $(BABEL_TRANSLATIONS)/%.pot : $(SRC)/.babel.cfg $(BABEL_INPUT)
$(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po : $(BABEL_TRANSLATIONS)/messages.pot $(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po : $(BABEL_TRANSLATIONS)/messages.pot
@mkdir -p $(@D) @mkdir -p $(@D)
@pybabel update --init-missing -i $< -d $(BABEL_TRANSLATIONS) -l $* @pybabel update --init-missing --ignore-pot-creation-date -i $< -d $(BABEL_TRANSLATIONS) -l $*
$(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.mo : $(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po $(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.mo : $(BABEL_TRANSLATIONS)/%/LC_MESSAGES/messages.po
@pybabel compile -d $(BABEL_TRANSLATIONS) -l $* @pybabel compile -d $(BABEL_TRANSLATIONS) -l $*

View File

@@ -29,17 +29,17 @@ target "_common" {
"type =sbom,generator=docker/scout-sbom-indexer:latest" "type =sbom,generator=docker/scout-sbom-indexer:latest"
] ]
annotations = [ annotations = [
"index:org.opencontainers.image.title=TSUN Gen3 Proxy", "index,manifest-descriptor:org.opencontainers.image.title=TSUN-Proxy",
"index:org.opencontainers.image.authors=Stefan Allius", "index,manifest-descriptor:org.opencontainers.image.authors=Stefan Allius",
"index:org.opencontainers.image.created=${BUILD_DATE}", "index,manifest-descriptor:org.opencontainers.image.created=${BUILD_DATE}",
"index:org.opencontainers.image.version=${VERSION}", "index,manifest-descriptor:org.opencontainers.image.version=${VERSION}",
"index:org.opencontainers.image.revision=${BRANCH}", "index,manifest-descriptor:org.opencontainers.image.revision=${BRANCH}",
"index:org.opencontainers.image.description=${DESCRIPTION}", "index,manifest-descriptor:org.opencontainers.image.description=${DESCRIPTION}",
"index:org.opencontainers.image.licenses=BSD-3-Clause", "index:org.opencontainers.image.licenses=BSD-3-Clause",
"index:org.opencontainers.image.source=https://github.com/s-allius/tsun-gen3-proxy" "index:org.opencontainers.image.source=https://github.com/s-allius/tsun-gen3-proxy"
] ]
labels = { labels = {
"org.opencontainers.image.title" = "TSUN Gen3 Proxy" "org.opencontainers.image.title" = "TSUN-Proxy"
"org.opencontainers.image.authors" = "Stefan Allius" "org.opencontainers.image.authors" = "Stefan Allius"
"org.opencontainers.image.created" = "${BUILD_DATE}" "org.opencontainers.image.created" = "${BUILD_DATE}"
"org.opencontainers.image.version" = "${VERSION}" "org.opencontainers.image.version" = "${VERSION}"
@@ -53,7 +53,7 @@ target "_common" {
] ]
no-cache = false no-cache = false
platforms = ["linux/amd64", "linux/arm64", "linux/arm/v7"] platforms = ["linux/amd64", "linux/arm64"]
} }
target "_debug" { target "_debug" {

View File

@@ -1,8 +1,8 @@
flake8==7.2.0 flake8==7.3.0
pytest==8.3.5 pytest==8.4.2
pytest-asyncio==0.26.0 pytest-asyncio==1.2.0
pytest-cov==6.1.1 pytest-cov==7.0.0
python-dotenv==1.1.0 python-dotenv==1.2.1
mock==5.2.0 mock==5.2.0
coverage==7.8.2 coverage==7.10.7
jinja2-cli==0.8.2 jinja2-cli==0.8.2

View File

@@ -327,6 +327,7 @@ class SolarmanV5(SolarmanBase):
self.sensor_list = 0 self.sensor_list = 0
self.mb_regs = [{'addr': 0x3000, 'len': 48}, self.mb_regs = [{'addr': 0x3000, 'len': 48},
{'addr': 0x2000, 'len': 96}] {'addr': 0x2000, 'len': 96}]
self.background_tasks = set()
''' '''
Our puplic methods Our puplic methods
@@ -339,11 +340,12 @@ class SolarmanV5(SolarmanBase):
self.inverter = None self.inverter = None
self.switch.clear() self.switch.clear()
self.log_lvl.clear() self.log_lvl.clear()
self.background_tasks.clear()
super().close() super().close()
async def send_start_cmd(self, snr: int, host: str, def send_start_cmd(self, snr: int, host: str,
forward: bool, forward: bool,
start_timeout=MB_CLIENT_DATA_UP): start_timeout=MB_CLIENT_DATA_UP):
self.no_forwarding = True self.no_forwarding = True
self.establish_inv_emu = forward self.establish_inv_emu = forward
self.snr = snr self.snr = snr
@@ -690,8 +692,10 @@ class SolarmanV5(SolarmanBase):
self.__forward_msg() self.__forward_msg()
def publish_mqtt(self, key, data): # pragma: no cover def publish_mqtt(self, key, data): # pragma: no cover
asyncio.ensure_future( task = asyncio.ensure_future(
Proxy.mqtt.publish(key, data)) Proxy.mqtt.publish(key, data))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
def get_cmd_rsp_log_lvl(self) -> int: def get_cmd_rsp_log_lvl(self) -> int:
ftype = self.ifc.rx_peek()[self.header_len] ftype = self.ifc.rx_peek()[self.header_len]

View File

@@ -4,6 +4,7 @@ import logging
import traceback import traceback
import json import json
import gc import gc
import socket
from aiomqtt import MqttCodeError from aiomqtt import MqttCodeError
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from ipaddress import ip_address from ipaddress import ip_address
@@ -38,6 +39,7 @@ class InverterBase(InverterIfc, Proxy):
self.use_emulation = False self.use_emulation = False
self.__ha_restarts = -1 self.__ha_restarts = -1
self.remote = StreamPtr(None) self.remote = StreamPtr(None)
self.background_tasks = set()
ifc = AsyncStreamServer(reader, writer, ifc = AsyncStreamServer(reader, writer,
self.async_publ_mqtt, self.async_publ_mqtt,
self.create_remote, self.create_remote,
@@ -72,6 +74,7 @@ class InverterBase(InverterIfc, Proxy):
if self.remote.ifc: if self.remote.ifc:
self.remote.ifc.close() self.remote.ifc.close()
self.remote.ifc = None self.remote.ifc = None
self.background_tasks.clear()
async def disc(self, shutdown_started=False) -> None: async def disc(self, shutdown_started=False) -> None:
if self.remote.stream: if self.remote.stream:
@@ -136,9 +139,14 @@ class InverterBase(InverterIfc, Proxy):
logging.info(f'[{self.remote.stream.node_id}:' logging.info(f'[{self.remote.stream.node_id}:'
f'{self.remote.stream.conn_no}] ' f'{self.remote.stream.conn_no}] '
f'Connected to {addr}') f'Connected to {addr}')
asyncio.create_task(self.remote.ifc.client_loop(addr)) task = asyncio.create_task(
self.remote.ifc.client_loop(addr))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
except (ConnectionRefusedError, TimeoutError) as error: except (ConnectionRefusedError,
TimeoutError,
socket.gaierror) as error:
logging.info(f'{error}') logging.info(f'{error}')
except Exception: except Exception:
Infos.inc_counter('SW_Exception') Infos.inc_counter('SW_Exception')

View File

@@ -193,7 +193,7 @@ class Message(ProtocolIfc):
return return
self.mb.build_msg(dev_id, func, addr, val, log_lvl) self.mb.build_msg(dev_id, func, addr, val, log_lvl)
async def send_modbus_cmd(self, func, addr, val, log_lvl) -> None: def send_modbus_cmd(self, func, addr, val, log_lvl) -> None:
self._send_modbus_cmd(Modbus.INV_ADDR, func, addr, val, log_lvl) self._send_modbus_cmd(Modbus.INV_ADDR, func, addr, val, log_lvl)
def _send_modbus_scan(self): def _send_modbus_scan(self):

View File

@@ -43,6 +43,7 @@ class ModbusTcp():
def __init__(self, loop, tim_restart=10) -> None: def __init__(self, loop, tim_restart=10) -> None:
self.tim_restart = tim_restart self.tim_restart = tim_restart
self.background_tasks = set()
inverters = Config.get('inverters') inverters = Config.get('inverters')
batteries = Config.get('batteries') batteries = Config.get('batteries')
@@ -54,10 +55,13 @@ class ModbusTcp():
and 'client_mode' in inv): and 'client_mode' in inv):
client = inv['client_mode'] client = inv['client_mode']
logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501 logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501
loop.create_task(self.modbus_loop(client['host'], task = loop.create_task(
client['port'], self.modbus_loop(client['host'],
inv['monitor_sn'], client['port'],
client['forward'])) inv['monitor_sn'],
client['forward']))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
async def modbus_loop(self, host, port, async def modbus_loop(self, host, port,
snr: int, forward: bool) -> None: snr: int, forward: bool) -> None:
@@ -66,7 +70,7 @@ class ModbusTcp():
try: try:
async with ModbusConn(host, port) as inverter: async with ModbusConn(host, port) as inverter:
stream = inverter.local.stream stream = inverter.local.stream
await stream.send_start_cmd(snr, host, forward) stream.send_start_cmd(snr, host, forward)
await stream.ifc.loop() await stream.ifc.loop()
logger.info(f'[{stream.node_id}:{stream.conn_no}] ' logger.info(f'[{stream.node_id}:{stream.conn_no}] '
f'Connection closed - Shutdown: ' f'Connection closed - Shutdown: '

View File

@@ -112,7 +112,7 @@ class Mqtt(metaclass=Singleton):
except asyncio.CancelledError: except asyncio.CancelledError:
logger_mqtt.debug("MQTT task cancelled") logger_mqtt.debug("MQTT task cancelled")
self.__client = None self.__client = None
return raise
except Exception: except Exception:
# self.inc_counter('SW_Exception') # fixme # self.inc_counter('SW_Exception') # fixme
self.ctime = None self.ctime = None
@@ -151,7 +151,7 @@ class Mqtt(metaclass=Singleton):
if self.__cb_mqtt_is_up: if self.__cb_mqtt_is_up:
await self.__cb_mqtt_is_up() await self.__cb_mqtt_is_up()
async def _out_coeff(self, message): def _out_coeff(self, message):
payload = message.payload.decode("UTF-8") payload = message.payload.decode("UTF-8")
try: try:
val = round(float(payload) * 1024/100) val = round(float(payload) * 1024/100)
@@ -160,9 +160,9 @@ class Mqtt(metaclass=Singleton):
'the range 0..100,' 'the range 0..100,'
f' got: {payload}') f' got: {payload}')
else: else:
await self._modbus_cmd(message, self._modbus_cmd(message,
Modbus.WRITE_SINGLE_REG, Modbus.WRITE_SINGLE_REG,
0, 0x202c, val) 0, 0x202c, val)
except Exception: except Exception:
pass pass
@@ -182,7 +182,7 @@ class Mqtt(metaclass=Singleton):
else: else:
logger_mqtt.warning(f'Node_id: {node_id} not found') logger_mqtt.warning(f'Node_id: {node_id} not found')
async def _modbus_cmd(self, message, func, params=0, addr=0, val=0): def _modbus_cmd(self, message, func, params=0, addr=0, val=0):
payload = message.payload.decode("UTF-8") payload = message.payload.decode("UTF-8")
for fnc in self.each_inverter(message, "send_modbus_cmd"): for fnc in self.each_inverter(message, "send_modbus_cmd"):
res = payload.split(',') res = payload.split(',')
@@ -195,7 +195,7 @@ class Mqtt(metaclass=Singleton):
elif params == 2: elif params == 2:
addr = int(res[0], base=16) addr = int(res[0], base=16)
val = int(res[1]) # lenght val = int(res[1]) # lenght
await fnc(func, addr, val, logging.INFO) fnc(func, addr, val, logging.INFO)
async def _at_cmd(self, message): async def _at_cmd(self, message):
payload = message.payload.decode("UTF-8") payload = message.payload.decode("UTF-8")

View File

@@ -12,7 +12,7 @@ class Schedule:
count = 0 count = 0
@classmethod @classmethod
def start(cls) -> None: def start(cls) -> None: # pragma: no cover
'''Start the scheduler and schedule the tasks (cron jobs)''' '''Start the scheduler and schedule the tasks (cron jobs)'''
logging.debug("Scheduler init") logging.debug("Scheduler init")
cls.mqtt = Mqtt(None) cls.mqtt = Mqtt(None)
@@ -20,7 +20,7 @@ class Schedule:
crontab('0 0 * * *', func=cls.atmidnight, start=True) crontab('0 0 * * *', func=cls.atmidnight, start=True)
@classmethod @classmethod
async def atmidnight(cls) -> None: async def atmidnight(cls) -> None: # pragma: no cover
'''Clear daily counters at midnight''' '''Clear daily counters at midnight'''
logging.info("Clear daily counters at midnight") logging.info("Clear daily counters at midnight")

View File

@@ -60,7 +60,16 @@ class Server():
@app.context_processor @app.context_processor
def utility_processor(): def utility_processor():
return dict(version=self.version) var = {'version': self.version,
'slug': os.getenv("SLUG"),
'hostname': os.getenv("HOSTNAME"),
}
if var['slug']:
var['hassio'] = True
slug_len = len(var['slug'])
var['addonname'] = var['slug'] + '_' + \
var['hostname'][slug_len+1:]
return var
def parse_args(self, arg_list: list[str] | None): def parse_args(self, arg_list: list[str] | None):
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@@ -209,6 +218,7 @@ app = Quart(__name__,
static_folder='web/static') static_folder='web/static')
app.secret_key = 'JKLdks.dajlKKKdladkflKwolafallsdfl' app.secret_key = 'JKLdks.dajlKKKdladkflKwolafallsdfl'
app.jinja_env.globals.update(url_for=url_for) app.jinja_env.globals.update(url_for=url_for)
app.background_tasks = set()
server = Server(app, __name__ == "__main__") server = Server(app, __name__ == "__main__")
Web(app, server.trans_path, server.rel_urls) Web(app, server.trans_path, server.rel_urls)
@@ -259,9 +269,13 @@ async def startup_app(): # pragma: no cover
for inv_class, port in [(InverterG3, 5005), (InverterG3P, 10000)]: for inv_class, port in [(InverterG3, 5005), (InverterG3P, 10000)]:
logging.info(f'listen on port: {port} for inverters') logging.info(f'listen on port: {port} for inverters')
loop.create_task(asyncio.start_server(lambda r, w, i=inv_class: task = loop.create_task(
handle_client(r, w, i), asyncio.start_server(lambda r, w, i=inv_class:
'0.0.0.0', port)) handle_client(r, w, i),
'0.0.0.0', port))
app.background_tasks.add(task)
task.add_done_callback(app.background_tasks.discard)
ProxyState.set_up(True) ProxyState.set_up(True)
@@ -285,6 +299,7 @@ async def handle_shutdown(): # pragma: no cover
await inverter.disc(True) await inverter.disc(True)
logging.info('Proxy disconnecting done') logging.info('Proxy disconnecting done')
app.background_tasks.clear()
await Proxy.class_close(loop) await Proxy.class_close(loop)

View File

@@ -29,9 +29,9 @@ def get_tz():
@web.context_processor @web.context_processor
def utility_processor(): def utility_processor():
return dict(lang=babel_get_locale(), return {'lang': babel_get_locale(),
lang_str=LANGUAGES.get(str(babel_get_locale()), "English"), 'lang_str': LANGUAGES.get(str(babel_get_locale()), "English"),
languages=LANGUAGES) 'languages': LANGUAGES}
@web.route('/language/<language>') @web.route('/language/<language>')

View File

@@ -22,3 +22,6 @@ class LogHandler(Handler, metaclass=Singleton):
def get_buffer(self, elms=0) -> list: def get_buffer(self, elms=0) -> list:
return list(self.buffer)[-elms:] return list(self.buffer)[-elms:]
def clear(self):
self.buffer.clear()

View File

@@ -7,3 +7,4 @@
.fa-rotate-right:before{content:"\f01e"} .fa-rotate-right:before{content:"\f01e"}
.fa-cloud-arrow-down-alt:before{content:"\f381"} .fa-cloud-arrow-down-alt:before{content:"\f381"}
.fa-cloud-arrow-up-alt:before{content:"\f382"} .fa-cloud-arrow-up-alt:before{content:"\f382"}
.fa-gear:before{content:"\f013"}

View File

@@ -59,6 +59,11 @@
<a href="{{ url_for('.mqtt')}}" class="w3-bar-item w3-button w3-padding {% block menu2_class %}{% endblock %}"><i class="fa fa-database fa-fw"></i>  MQTT</a> <a href="{{ url_for('.mqtt')}}" class="w3-bar-item w3-button w3-padding {% block menu2_class %}{% endblock %}"><i class="fa fa-database fa-fw"></i>  MQTT</a>
<a href="{{ url_for('.notes')}}" class="w3-bar-item w3-button w3-padding {% block menu3_class %}{% endblock %}"><i class="fa fa-info fa-fw"></i>  {{_('Important Messages')}}</a> <a href="{{ url_for('.notes')}}" class="w3-bar-item w3-button w3-padding {% block menu3_class %}{% endblock %}"><i class="fa fa-info fa-fw"></i>  {{_('Important Messages')}}</a>
<a href="{{ url_for('.logging')}}" class="w3-bar-item w3-button w3-padding {% block menu4_class %}{% endblock %}"><i class="fa fa-file-export fa-fw"></i>  {{_('Log Files')}}</a> <a href="{{ url_for('.logging')}}" class="w3-bar-item w3-button w3-padding {% block menu4_class %}{% endblock %}"><i class="fa fa-file-export fa-fw"></i>  {{_('Log Files')}}</a>
{% if hassio is defined %}
<br>
<a href="/hassio/addon/{{addonname}}/config" target="_top" class="w3-bar-item w3-button w3-padding"><i class="fa fa-gear fa-fw"></i>  {{_('Add-on Config')}}</a>
<a href="/hassio/addon/{{addonname}}/logs" target="_top" class="w3-bar-item w3-button w3-padding"><i class="fa fa-file fa-fw"></i>  {{_('Add-on Log')}}</a>
{% endif %}
</div> </div>
</nav> </nav>

View File

@@ -1,19 +1,19 @@
2025-04-30 00:01:23 INFO | root | Server "proxy - unknown" will be started 2025-04-30 00:01:23 INFO | root | Server "proxy - unknown" will be started
2025-04-30 00:01:23 INFO | root | current dir: /Users/sallius/tsun/tsun-gen3-proxy 2025-04-30 00:01:24 INFO | root | current dir: /Users/sallius/tsun/tsun-gen3-proxy
2025-04-30 00:01:23 INFO | root | config_path: ./config/ 2025-04-30 00:01:25 INFO | root | config_path: ./config/
2025-04-30 00:01:23 INFO | root | json_config: None 2025-04-30 00:01:26 INFO | root | json_config: None
2025-04-30 00:01:23 INFO | root | toml_config: None 2025-04-30 00:01:27 INFO | root | toml_config: None
2025-04-30 00:01:23 INFO | root | trans_path: ../translations/ 2025-04-30 00:01:28 INFO | root | trans_path: ../translations/
2025-04-30 00:01:23 INFO | root | rel_urls: False 2025-04-30 00:01:29 INFO | root | rel_urls: False
2025-04-30 00:01:23 INFO | root | log_path: ./log/ 2025-04-30 00:01:30 INFO | root | log_path: ./log/
2025-04-30 00:01:23 INFO | root | log_backups: unlimited 2025-04-30 00:01:31 INFO | root | log_backups: unlimited
2025-04-30 00:01:23 INFO | root | LOG_LVL : None 2025-04-30 00:01:32 INFO | root | LOG_LVL : None
2025-04-30 00:01:23 INFO | root | ****** 2025-04-30 00:01:33 INFO | root | ******
2025-04-30 00:01:23 INFO | root | Read from /Users/sallius/tsun/tsun-gen3-proxy/app/src/cnf/default_config.toml => ok 2025-04-30 00:01:34 INFO | root | Read from /Users/sallius/tsun/tsun-gen3-proxy/app/src/cnf/default_config.toml => ok
2025-04-30 00:01:23 INFO | root | Read from environment => ok 2025-04-30 00:01:35 INFO | root | Read from environment => ok
2025-04-30 00:01:23 INFO | root | Read from ./config/config.json => n/a 2025-04-30 00:01:36 INFO | root | Read from ./config/config.json => n/a
2025-04-30 00:01:23 INFO | root | Read from ./config/config.toml => n/a 2025-04-30 00:01:37 INFO | root | Read from ./config/config.toml => n/a
2025-04-30 00:01:23 INFO | root | ****** 2025-04-30 00:01:38 INFO | root | ******
2025-04-30 00:01:23 INFO | root | listen on port: 5005 for inverters 2025-04-30 00:01:39 INFO | root | listen on port: 5005 for inverters
2025-04-30 00:01:23 INFO | root | listen on port: 10000 for inverters 2025-04-30 00:01:40 INFO | root | listen on port: 10000 for inverters
2025-04-30 00:01:23 INFO | root | Start Quart 2025-04-30 00:01:41 INFO | root | Start Quart

View File

@@ -82,7 +82,7 @@ def spy_inc_cnt():
yield infos yield infos
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_close_cb(): async def test_close_cb():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -122,7 +122,7 @@ async def test_close_cb():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read(): async def test_read():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -161,7 +161,7 @@ async def test_read():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_write(): async def test_write():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -204,7 +204,7 @@ async def test_write():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_publ_mqtt_cb(): async def test_publ_mqtt_cb():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -235,7 +235,7 @@ async def test_publ_mqtt_cb():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_create_remote_cb(): async def test_create_remote_cb():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -268,7 +268,7 @@ async def test_create_remote_cb():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_sw_exception(): async def test_sw_exception():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -300,7 +300,7 @@ async def test_sw_exception():
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_os_error(): async def test_os_error():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
reader = FakeReader() reader = FakeReader()
@@ -371,7 +371,7 @@ def create_remote(remote, test_type, with_close_hdr:bool = False):
remote.ifc.prot_set_init_new_client_conn_cb(callback) remote.ifc.prot_set_init_new_client_conn_cb(callback)
remote.stream = FakeProto(remote.ifc, False) remote.stream = FakeProto(remote.ifc, False)
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward(): async def test_forward():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -393,7 +393,7 @@ async def test_forward():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_with_conn(): async def test_forward_with_conn():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -411,7 +411,7 @@ async def test_forward_with_conn():
assert cnt == 0 assert cnt == 0
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_no_conn(): async def test_forward_no_conn():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -428,7 +428,7 @@ async def test_forward_no_conn():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_sw_except(): async def test_forward_sw_except():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -446,7 +446,7 @@ async def test_forward_sw_except():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_os_error(): async def test_forward_os_error():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -464,7 +464,7 @@ async def test_forward_os_error():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_os_error2(): async def test_forward_os_error2():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -482,7 +482,7 @@ async def test_forward_os_error2():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_os_error3(): async def test_forward_os_error3():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -500,7 +500,7 @@ async def test_forward_os_error3():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_runtime_error(): async def test_forward_runtime_error():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -518,7 +518,7 @@ async def test_forward_runtime_error():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_runtime_error2(): async def test_forward_runtime_error2():
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -536,7 +536,7 @@ async def test_forward_runtime_error2():
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_runtime_error3(spy_inc_cnt): async def test_forward_runtime_error3(spy_inc_cnt):
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -558,7 +558,7 @@ async def test_forward_runtime_error3(spy_inc_cnt):
assert cnt == 1 assert cnt == 1
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_resp(spy_inc_cnt): async def test_forward_resp(spy_inc_cnt):
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)
@@ -581,7 +581,7 @@ async def test_forward_resp(spy_inc_cnt):
del ifc del ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_forward_resp2(spy_inc_cnt): async def test_forward_resp2(spy_inc_cnt):
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
remote = StreamPtr(None) remote = StreamPtr(None)

View File

@@ -113,7 +113,7 @@ def patch_unhealthy_remote():
with patch.object(AsyncStreamClient, 'healthy', new_healthy) as conn: with patch.object(AsyncStreamClient, 'healthy', new_healthy) as conn:
yield conn yield conn
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_inverter_iter(my_loop): async def test_inverter_iter(my_loop):
_ = my_loop _ = my_loop
InverterBase._registry.clear() InverterBase._registry.clear()
@@ -217,7 +217,7 @@ def test_unhealthy_remote(patch_unhealthy_remote):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn(my_loop, config_conn, patch_open_connection): async def test_remote_conn(my_loop, config_conn, patch_open_connection):
_ = my_loop _ = my_loop
_ = config_conn _ = config_conn
@@ -244,7 +244,7 @@ async def test_remote_conn(my_loop, config_conn, patch_open_connection):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn_to_private(my_loop, config_conn, patch_open_connection): async def test_remote_conn_to_private(my_loop, config_conn, patch_open_connection):
'''check DNS resolving of the TSUN FQDN to a local address''' '''check DNS resolving of the TSUN FQDN to a local address'''
_ = my_loop _ = my_loop
@@ -283,7 +283,7 @@ async def test_remote_conn_to_private(my_loop, config_conn, patch_open_connectio
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn_to_loopback(my_loop, config_conn, patch_open_connection): async def test_remote_conn_to_loopback(my_loop, config_conn, patch_open_connection):
'''check DNS resolving of the TSUN FQDN to the loopback address''' '''check DNS resolving of the TSUN FQDN to the loopback address'''
_ = my_loop _ = my_loop
@@ -321,7 +321,7 @@ async def test_remote_conn_to_loopback(my_loop, config_conn, patch_open_connecti
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn_to_none(my_loop, config_conn, patch_open_connection): async def test_remote_conn_to_none(my_loop, config_conn, patch_open_connection):
'''check if get_extra_info() return None in case of an error''' '''check if get_extra_info() return None in case of an error'''
_ = my_loop _ = my_loop
@@ -359,7 +359,7 @@ async def test_remote_conn_to_none(my_loop, config_conn, patch_open_connection):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_unhealthy_remote(my_loop, config_conn, patch_open_connection, patch_unhealthy_remote): async def test_unhealthy_remote(my_loop, config_conn, patch_open_connection, patch_unhealthy_remote):
_ = my_loop _ = my_loop
_ = config_conn _ = config_conn
@@ -397,7 +397,7 @@ async def test_unhealthy_remote(my_loop, config_conn, patch_open_connection, pat
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_disc(my_loop, config_conn, patch_open_connection): async def test_remote_disc(my_loop, config_conn, patch_open_connection):
_ = my_loop _ = my_loop
_ = config_conn _ = config_conn

View File

@@ -99,7 +99,7 @@ def patch_healthy():
with patch.object(AsyncStream, 'healthy') as conn: with patch.object(AsyncStream, 'healthy') as conn:
yield conn yield conn
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_method_calls(my_loop, patch_healthy): async def test_method_calls(my_loop, patch_healthy):
spy = patch_healthy spy = patch_healthy
reader = FakeReader() reader = FakeReader()
@@ -119,7 +119,7 @@ async def test_method_calls(my_loop, patch_healthy):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn(my_loop, config_conn, patch_open_connection): async def test_remote_conn(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -137,7 +137,7 @@ async def test_remote_conn(my_loop, config_conn, patch_open_connection):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_except(my_loop, config_conn, patch_open_connection): async def test_remote_except(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -164,7 +164,7 @@ async def test_remote_except(my_loop, config_conn, patch_open_connection):
cnt += 1 cnt += 1
assert cnt == 0 assert cnt == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_publish(my_loop, config_conn, patch_open_connection): async def test_mqtt_publish(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -191,7 +191,7 @@ async def test_mqtt_publish(my_loop, config_conn, patch_open_connection):
await inverter.async_publ_mqtt() await inverter.async_publ_mqtt()
assert Infos.new_stat_data['proxy'] == False assert Infos.new_stat_data['proxy'] == False
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_err): async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_err):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -208,7 +208,7 @@ async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_
await inverter.async_publ_mqtt() await inverter.async_publ_mqtt()
assert stream.new_data['inverter'] == True assert stream.new_data['inverter'] == True
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_except(my_loop, config_conn, patch_open_connection, patch_mqtt_except): async def test_mqtt_except(my_loop, config_conn, patch_open_connection, patch_mqtt_except):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection

View File

@@ -94,7 +94,7 @@ def patch_open_connection():
with patch.object(asyncio, 'open_connection', new_open) as conn: with patch.object(asyncio, 'open_connection', new_open) as conn:
yield conn yield conn
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_method_calls(my_loop, config_conn): async def test_method_calls(my_loop, config_conn):
_ = config_conn _ = config_conn
reader = FakeReader() reader = FakeReader()
@@ -105,7 +105,7 @@ async def test_method_calls(my_loop, config_conn):
assert inverter.local.stream assert inverter.local.stream
assert inverter.local.ifc assert inverter.local.ifc
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_conn(my_loop, config_conn, patch_open_connection): async def test_remote_conn(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -116,7 +116,7 @@ async def test_remote_conn(my_loop, config_conn, patch_open_connection):
await asyncio.sleep(0) await asyncio.sleep(0)
assert inverter.remote.stream assert inverter.remote.stream
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_remote_except(my_loop, config_conn, patch_open_connection): async def test_remote_except(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -138,7 +138,7 @@ async def test_remote_except(my_loop, config_conn, patch_open_connection):
test = MockType.RD_TEST_0_BYTES test = MockType.RD_TEST_0_BYTES
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_publish(my_loop, config_conn, patch_open_connection): async def test_mqtt_publish(my_loop, config_conn, patch_open_connection):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -165,7 +165,7 @@ async def test_mqtt_publish(my_loop, config_conn, patch_open_connection):
await inverter.async_publ_mqtt() await inverter.async_publ_mqtt()
assert Infos.new_stat_data['proxy'] == False assert Infos.new_stat_data['proxy'] == False
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_err): async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_err):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection
@@ -182,7 +182,7 @@ async def test_mqtt_err(my_loop, config_conn, patch_open_connection, patch_mqtt_
await inverter.async_publ_mqtt() await inverter.async_publ_mqtt()
assert stream.new_data['inverter'] == True assert stream.new_data['inverter'] == True
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_except(my_loop, config_conn, patch_open_connection, patch_mqtt_except): async def test_mqtt_except(my_loop, config_conn, patch_open_connection, patch_mqtt_except):
_ = config_conn _ = config_conn
_ = patch_open_connection _ = patch_open_connection

View File

@@ -19,7 +19,7 @@ class ModbusTestHelper(Modbus):
def resp_handler(self): def resp_handler(self):
self.recv_responses += 1 self.recv_responses += 1
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_crc(): async def test_modbus_crc():
'''Check CRC-16 calculation''' '''Check CRC-16 calculation'''
mb = Modbus(None) mb = Modbus(None)
@@ -38,7 +38,7 @@ async def test_modbus_crc():
msg += b'\x00\x00\x00\x00\x00\x00\x00\xe6\xef' msg += b'\x00\x00\x00\x00\x00\x00\x00\xe6\xef'
assert 0 == mb._Modbus__calc_crc(msg) assert 0 == mb._Modbus__calc_crc(msg)
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modbus_pdu(): async def test_build_modbus_pdu():
'''Check building and sending a MODBUS RTU''' '''Check building and sending a MODBUS RTU'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -51,7 +51,7 @@ async def test_build_modbus_pdu():
assert mb.last_len == 18 assert mb.last_len == 18
assert mb.err == 0 assert mb.err == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_req(): async def test_recv_req():
'''Receive a valid request, which must transmitted''' '''Receive a valid request, which must transmitted'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -61,7 +61,7 @@ async def test_recv_req():
assert mb.last_len == 0x12 assert mb.last_len == 0x12
assert mb.err == 0 assert mb.err == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_req_crc_err(): async def test_recv_req_crc_err():
'''Receive a request with invalid CRC, which must be dropped''' '''Receive a request with invalid CRC, which must be dropped'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -72,7 +72,7 @@ async def test_recv_req_crc_err():
assert mb.last_len == 0 assert mb.last_len == 0
assert mb.err == 1 assert mb.err == 1
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_resp_crc_err(): async def test_recv_resp_crc_err():
'''Receive a response with invalid CRC, which must be dropped''' '''Receive a response with invalid CRC, which must be dropped'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -94,7 +94,7 @@ async def test_recv_resp_crc_err():
mb._Modbus__stop_timer() mb._Modbus__stop_timer()
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_resp_invalid_addr(): async def test_recv_resp_invalid_addr():
'''Receive a response with wrong server addr, which must be dropped''' '''Receive a response with wrong server addr, which must be dropped'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -119,7 +119,7 @@ async def test_recv_resp_invalid_addr():
mb._Modbus__stop_timer() mb._Modbus__stop_timer()
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_recv_fcode(): async def test_recv_recv_fcode():
'''Receive a response with wrong function code, which must be dropped''' '''Receive a response with wrong function code, which must be dropped'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -142,7 +142,7 @@ async def test_recv_recv_fcode():
mb._Modbus__stop_timer() mb._Modbus__stop_timer()
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_resp_len(): async def test_recv_resp_len():
'''Receive a response with wrong data length, which must be dropped''' '''Receive a response with wrong data length, which must be dropped'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -166,7 +166,7 @@ async def test_recv_resp_len():
mb._Modbus__stop_timer() mb._Modbus__stop_timer()
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_unexpect_resp(): async def test_recv_unexpect_resp():
'''Receive a response when we havb't sent a request''' '''Receive a response when we havb't sent a request'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -183,7 +183,7 @@ async def test_recv_unexpect_resp():
assert mb.req_pend == False assert mb.req_pend == False
assert mb.que.qsize() == 0 assert mb.que.qsize() == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_parse_resp(): async def test_parse_resp():
'''Receive matching response and parse the values''' '''Receive matching response and parse the values'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -210,7 +210,7 @@ async def test_parse_resp():
assert mb.que.qsize() == 0 assert mb.que.qsize() == 0
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_queue(): async def test_queue():
mb = ModbusTestHelper() mb = ModbusTestHelper()
mb.build_msg(1,3,0x3022,4) mb.build_msg(1,3,0x3022,4)
@@ -229,7 +229,7 @@ async def test_queue():
mb._Modbus__stop_timer() mb._Modbus__stop_timer()
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_queue2(): async def test_queue2():
'''Check queue handling for build_msg() calls''' '''Check queue handling for build_msg() calls'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -279,7 +279,7 @@ async def test_queue2():
assert mb.que.qsize() == 0 assert mb.que.qsize() == 0
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_queue3(): async def test_queue3():
'''Check queue handling for recv_req() calls''' '''Check queue handling for recv_req() calls'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -336,7 +336,7 @@ async def test_queue3():
assert mb.que.qsize() == 0 assert mb.que.qsize() == 0
assert not mb.req_pend assert not mb.req_pend
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_timeout(my_loop): async def test_timeout(my_loop):
'''Test MODBUS response timeout and RTU retransmitting''' '''Test MODBUS response timeout and RTU retransmitting'''
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -384,7 +384,7 @@ async def test_timeout(my_loop):
assert mb.retry_cnt == 0 assert mb.retry_cnt == 0
assert mb.send_calls == 4 assert mb.send_calls == 4
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_recv_unknown_data(): async def test_recv_unknown_data():
'''Receive a response with an unknwon register''' '''Receive a response with an unknwon register'''
mb = ModbusTestHelper() mb = ModbusTestHelper()
@@ -404,7 +404,7 @@ async def test_recv_unknown_data():
del mb.mb_reg_mapping[0x9000] del mb.mb_reg_mapping[0x9000]
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_close(): async def test_close():
'''Check queue handling for build_msg() calls''' '''Check queue handling for build_msg() calls'''
mb = ModbusTestHelper() mb = ModbusTestHelper()

View File

@@ -189,7 +189,7 @@ def patch_mqtt_except():
with patch.object(Mqtt, 'publish', new_publish) as conn: with patch.object(Mqtt, 'publish', new_publish) as conn:
yield conn yield conn
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_conn(config_conn, patch_open): async def test_modbus_conn(config_conn, patch_open):
_ = config_conn _ = config_conn
_ = patch_open _ = patch_open
@@ -209,7 +209,7 @@ async def test_modbus_conn(config_conn, patch_open):
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_no_cnf(): async def test_modbus_no_cnf():
_ = config_conn _ = config_conn
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@@ -217,7 +217,7 @@ async def test_modbus_no_cnf():
ModbusTcp(loop) ModbusTcp(loop)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_timeout(config_conn, patch_open_timeout): async def test_modbus_timeout(config_conn, patch_open_timeout):
_ = config_conn _ = config_conn
_ = patch_open_timeout _ = patch_open_timeout
@@ -235,7 +235,7 @@ async def test_modbus_timeout(config_conn, patch_open_timeout):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_value_err(config_conn, patch_open_value_error): async def test_modbus_value_err(config_conn, patch_open_value_error):
_ = config_conn _ = config_conn
_ = patch_open_value_error _ = patch_open_value_error
@@ -253,7 +253,7 @@ async def test_modbus_value_err(config_conn, patch_open_value_error):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_conn_abort(config_conn, patch_open_conn_abort): async def test_modbus_conn_abort(config_conn, patch_open_conn_abort):
_ = config_conn _ = config_conn
_ = patch_open_conn_abort _ = patch_open_conn_abort
@@ -271,7 +271,7 @@ async def test_modbus_conn_abort(config_conn, patch_open_conn_abort):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_cnf2(config_conn, patch_no_mqtt, patch_open): async def test_modbus_cnf2(config_conn, patch_no_mqtt, patch_open):
_ = config_conn _ = config_conn
_ = patch_open _ = patch_open
@@ -295,7 +295,7 @@ async def test_modbus_cnf2(config_conn, patch_no_mqtt, patch_open):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_cnf3(config_conn, patch_no_mqtt, patch_open): async def test_modbus_cnf3(config_conn, patch_no_mqtt, patch_open):
_ = config_conn _ = config_conn
_ = patch_open _ = patch_open
@@ -326,7 +326,7 @@ async def test_modbus_cnf3(config_conn, patch_no_mqtt, patch_open):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_err(config_conn, patch_mqtt_err, patch_open): async def test_mqtt_err(config_conn, patch_mqtt_err, patch_open):
_ = config_conn _ = config_conn
_ = patch_open _ = patch_open
@@ -357,7 +357,7 @@ async def test_mqtt_err(config_conn, patch_mqtt_err, patch_open):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
assert Infos.stat['proxy']['Inverter_Cnt'] == 0 assert Infos.stat['proxy']['Inverter_Cnt'] == 0
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_except(config_conn, patch_mqtt_except, patch_open): async def test_mqtt_except(config_conn, patch_mqtt_except, patch_open):
_ = config_conn _ = config_conn
_ = patch_open _ = patch_open

View File

@@ -132,7 +132,7 @@ def test_native_client(test_hostname, test_port):
finally: finally:
c.loop_stop() c.loop_stop()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_connection(config_mqtt_conn): async def test_mqtt_connection(config_mqtt_conn):
if NO_MOSQUITTO_TEST: if NO_MOSQUITTO_TEST:
pytest.skip('skipping, since Mosquitto is not reliable at the moment') pytest.skip('skipping, since Mosquitto is not reliable at the moment')
@@ -157,7 +157,7 @@ async def test_mqtt_connection(config_mqtt_conn):
await m.close() await m.close()
await m.publish('homeassistant/status', 'online') await m.publish('homeassistant/status', 'online')
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_ha_reconnect(config_mqtt_conn): async def test_ha_reconnect(config_mqtt_conn):
if NO_MOSQUITTO_TEST: if NO_MOSQUITTO_TEST:
pytest.skip('skipping, since Mosquitto is not reliable at the moment') pytest.skip('skipping, since Mosquitto is not reliable at the moment')
@@ -181,14 +181,18 @@ async def test_ha_reconnect(config_mqtt_conn):
assert m.received == 2 assert m.received == 2
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_no_config(config_no_conn): async def test_mqtt_no_config(config_no_conn, monkeypatch):
_ = config_no_conn _ = config_no_conn
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
on_connect = asyncio.Event() on_connect = asyncio.Event()
async def cb(): async def cb():
on_connect.set() on_connect.set()
async def my_publish(*args):
return
monkeypatch.setattr(aiomqtt.Client, "publish", my_publish)
try: try:
m = Mqtt(cb) m = Mqtt(cb)
@@ -197,15 +201,15 @@ async def test_mqtt_no_config(config_no_conn):
assert not on_connect.is_set() assert not on_connect.is_set()
try: try:
await m.publish('homeassistant/status', 'online') await m.publish('homeassistant/status', 'online')
assert False assert m.published == 1
except Exception: except Exception:
pass assert False
except TimeoutError: except TimeoutError:
assert False assert False
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_except_no_config(config_no_conn, monkeypatch, caplog): async def test_mqtt_except_no_config(config_no_conn, monkeypatch, caplog):
_ = config_no_conn _ = config_no_conn
@@ -235,7 +239,7 @@ async def test_mqtt_except_no_config(config_no_conn, monkeypatch, caplog):
await m.close() await m.close()
assert 'Connection lost; Reconnecting in 5 seconds' in caplog.text assert 'Connection lost; Reconnecting in 5 seconds' in caplog.text
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_except_def_config(config_def_conn, monkeypatch, caplog): async def test_mqtt_except_def_config(config_def_conn, monkeypatch, caplog):
_ = config_def_conn _ = config_def_conn
@@ -270,7 +274,7 @@ async def test_mqtt_except_def_config(config_def_conn, monkeypatch, caplog):
await m.close() await m.close()
assert 'MQTT is unconfigured; Check your config.toml!' in caplog.text assert 'MQTT is unconfigured; Check your config.toml!' in caplog.text
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_dispatch(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd): async def test_mqtt_dispatch(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd):
_ = config_mqtt_conn _ = config_mqtt_conn
_ = aiomqtt_mock _ = aiomqtt_mock
@@ -282,23 +286,23 @@ async def test_mqtt_dispatch(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd):
assert m.ha_restarts == 1 assert m.ha_restarts == 1
await m.receive(topic= 'tsun/inv_1/rated_load', payload= b'2') await m.receive(topic= 'tsun/inv_1/rated_load', payload= b'2')
spy.assert_awaited_once_with(Modbus.WRITE_SINGLE_REG, 0x2008, 2, logging.INFO) spy.assert_called_once_with(Modbus.WRITE_SINGLE_REG, 0x2008, 2, logging.INFO)
spy.reset_mock() spy.reset_mock()
await m.receive(topic= 'tsun/inv_1/out_coeff', payload= b'100') await m.receive(topic= 'tsun/inv_1/out_coeff', payload= b'100')
spy.assert_awaited_once_with(Modbus.WRITE_SINGLE_REG, 0x202c, 1024, logging.INFO) spy.assert_called_once_with(Modbus.WRITE_SINGLE_REG, 0x202c, 1024, logging.INFO)
spy.reset_mock() spy.reset_mock()
await m.receive(topic= 'tsun/inv_1/out_coeff', payload= b'50') await m.receive(topic= 'tsun/inv_1/out_coeff', payload= b'50')
spy.assert_awaited_once_with(Modbus.WRITE_SINGLE_REG, 0x202c, 512, logging.INFO) spy.assert_called_once_with(Modbus.WRITE_SINGLE_REG, 0x202c, 512, logging.INFO)
spy.reset_mock() spy.reset_mock()
await m.receive(topic= 'tsun/inv_1/modbus_read_regs', payload= b'0x3000, 10') await m.receive(topic= 'tsun/inv_1/modbus_read_regs', payload= b'0x3000, 10')
spy.assert_awaited_once_with(Modbus.READ_REGS, 0x3000, 10, logging.INFO) spy.assert_called_once_with(Modbus.READ_REGS, 0x3000, 10, logging.INFO)
spy.reset_mock() spy.reset_mock()
await m.receive(topic= 'tsun/inv_1/modbus_read_inputs', payload= b'0x3000, 10') await m.receive(topic= 'tsun/inv_1/modbus_read_inputs', payload= b'0x3000, 10')
spy.assert_awaited_once_with(Modbus.READ_INPUTS, 0x3000, 10, logging.INFO) spy.assert_called_once_with(Modbus.READ_INPUTS, 0x3000, 10, logging.INFO)
# test dispatching with empty mapping table # test dispatching with empty mapping table
m.topic_defs.clear() m.topic_defs.clear()
@@ -322,7 +326,29 @@ async def test_mqtt_dispatch(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_dispatch_cb(config_mqtt_conn, aiomqtt_mock):
_ = config_mqtt_conn
_ = aiomqtt_mock
on_connect = asyncio.Event()
async def cb():
on_connect.set()
try:
m = Mqtt(cb)
assert m.ha_restarts == 0
await m.receive('homeassistant/status', b'online') # send the message
assert on_connect.is_set()
assert m.ha_restarts == 1
except MqttError:
assert False
except Exception:
assert False
finally:
await m.close()
@pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_dispatch_err(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd, caplog): async def test_mqtt_dispatch_err(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd, caplog):
_ = config_mqtt_conn _ = config_mqtt_conn
_ = aiomqtt_mock _ = aiomqtt_mock
@@ -365,7 +391,7 @@ async def test_mqtt_dispatch_err(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd,
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_ignore_client_conn(config_mqtt_conn, spy_modbus_cmd_client): async def test_msg_ignore_client_conn(config_mqtt_conn, spy_modbus_cmd_client):
'''don't call function if connnection is not in server mode''' '''don't call function if connnection is not in server mode'''
_ = config_mqtt_conn _ = config_mqtt_conn
@@ -378,7 +404,7 @@ async def test_msg_ignore_client_conn(config_mqtt_conn, spy_modbus_cmd_client):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_ignore_unknown_func(config_mqtt_conn): async def test_ignore_unknown_func(config_mqtt_conn):
'''don't dispatch for unknwon function names''' '''don't dispatch for unknwon function names'''
_ = config_mqtt_conn _ = config_mqtt_conn
@@ -390,7 +416,7 @@ async def test_ignore_unknown_func(config_mqtt_conn):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_at_cmd_dispatch(config_mqtt_conn, spy_at_cmd): async def test_at_cmd_dispatch(config_mqtt_conn, spy_at_cmd):
_ = config_mqtt_conn _ = config_mqtt_conn
spy = spy_at_cmd spy = spy_at_cmd
@@ -403,7 +429,7 @@ async def test_at_cmd_dispatch(config_mqtt_conn, spy_at_cmd):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_dcu_dispatch(config_mqtt_conn, spy_dcu_cmd): async def test_dcu_dispatch(config_mqtt_conn, spy_dcu_cmd):
_ = config_mqtt_conn _ = config_mqtt_conn
spy = spy_dcu_cmd spy = spy_dcu_cmd
@@ -415,7 +441,7 @@ async def test_dcu_dispatch(config_mqtt_conn, spy_dcu_cmd):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_dcu_inv_value(config_mqtt_conn, spy_dcu_cmd): async def test_dcu_inv_value(config_mqtt_conn, spy_dcu_cmd):
_ = config_mqtt_conn _ = config_mqtt_conn
spy = spy_dcu_cmd spy = spy_dcu_cmd

View File

@@ -59,7 +59,7 @@ def config_conn(test_hostname, test_port):
} }
} }
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_inverter_cb(config_conn): async def test_inverter_cb(config_conn):
_ = config_conn _ = config_conn
@@ -72,7 +72,7 @@ async def test_inverter_cb(config_conn):
await Proxy._cb_mqtt_is_up() await Proxy._cb_mqtt_is_up()
spy.assert_called_once() spy.assert_called_once()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_is_up(config_conn): async def test_mqtt_is_up(config_conn):
_ = config_conn _ = config_conn
@@ -81,7 +81,7 @@ async def test_mqtt_is_up(config_conn):
await Proxy._cb_mqtt_is_up() await Proxy._cb_mqtt_is_up()
spy.assert_called() spy.assert_called()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_proxy_statt_invalid(config_conn): async def test_mqtt_proxy_statt_invalid(config_conn):
_ = config_conn _ = config_conn

View File

@@ -4,6 +4,10 @@ import logging
import os import os
from mock import patch from mock import patch
from server import app, Server, ProxyState, HypercornLogHndl from server import app, Server, ProxyState, HypercornLogHndl
from inverter_base import InverterBase
from gen3.talent import Talent
from test_inverter_base import FakeReader, FakeWriter
pytest_plugins = ('pytest_asyncio',) pytest_plugins = ('pytest_asyncio',)
@@ -108,20 +112,20 @@ class TestServerClass:
assert logging.getLogger('hypercorn.access').level == logging.INFO assert logging.getLogger('hypercorn.access').level == logging.INFO
assert logging.getLogger('hypercorn.error').level == logging.INFO assert logging.getLogger('hypercorn.error').level == logging.INFO
os.environ["LOG_LVL"] = "WARN" with patch.dict(os.environ, {'LOG_LVL': 'WARN'}):
s.parse_args(['--log_backups', '3']) s.parse_args(['--log_backups', '3'])
s.init_logging_system() s.init_logging_system()
assert s.log_backups == 3 assert s.log_backups == 3
assert s.log_level == logging.WARNING assert s.log_level == logging.WARNING
assert logging.handlers.log_backups == 3 assert logging.handlers.log_backups == 3
assert logging.getLogger().level == s.log_level assert logging.getLogger().level == s.log_level
assert logging.getLogger('msg').level == s.log_level assert logging.getLogger('msg').level == s.log_level
assert logging.getLogger('conn').level == s.log_level assert logging.getLogger('conn').level == s.log_level
assert logging.getLogger('data').level == s.log_level assert logging.getLogger('data').level == s.log_level
assert logging.getLogger('tracer').level == s.log_level assert logging.getLogger('tracer').level == s.log_level
assert logging.getLogger('asyncio').level == s.log_level assert logging.getLogger('asyncio').level == s.log_level
assert logging.getLogger('hypercorn.access').level == logging.INFO assert logging.getLogger('hypercorn.access').level == logging.INFO
assert logging.getLogger('hypercorn.error').level == logging.INFO assert logging.getLogger('hypercorn.error').level == logging.INFO
def test_build_config_error(self, caplog): def test_build_config_error(self, caplog):
s = self.FakeServer() s = self.FakeServer()
@@ -182,11 +186,12 @@ class TestHypercornLogHndl:
class TestApp: class TestApp:
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_ready(self): async def test_ready(self):
"""Test the ready route.""" """Test the ready route."""
ProxyState.set_up(False) ProxyState.set_up(False)
app.testing = True
client = app.test_client() client = app.test_client()
response = await client.get('/-/ready') response = await client.get('/-/ready')
assert response.status_code == 503 assert response.status_code == 503
@@ -199,20 +204,87 @@ class TestApp:
result = await response.get_data() result = await response.get_data()
assert result == b"Is ready" assert result == b"Is ready"
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_healthy(self): async def test_healthy(self):
"""Test the healthy route.""" """Test the healthy route."""
reader = FakeReader()
writer = FakeWriter()
ProxyState.set_up(False) with InverterBase(reader, writer, 'tsun', Talent):
client = app.test_client() ProxyState.set_up(False)
response = await client.get('/-/healthy') app.testing = True
assert response.status_code == 200 client = app.test_client()
result = await response.get_data() response = await client.get('/-/healthy')
assert result == b"I'm fine" assert response.status_code == 200
result = await response.get_data()
assert result == b"I'm fine"
ProxyState.set_up(True) ProxyState.set_up(True)
response = await client.get('/-/healthy') response = await client.get('/-/healthy')
assert response.status_code == 200 assert response.status_code == 200
result = await response.get_data() result = await response.get_data()
assert result == b"I'm fine" assert result == b"I'm fine"
@pytest.mark.asyncio(loop_scope="session")
async def test_unhealthy(self, monkeypatch, caplog):
"""Test the healthy route."""
def result_false(self):
return False
LOGGER = logging.getLogger("mqtt")
LOGGER.propagate = True
LOGGER.setLevel(logging.INFO)
monkeypatch.setattr(InverterBase, "healthy", result_false)
InverterBase._registry.clear()
reader = FakeReader()
writer = FakeWriter()
with caplog.at_level(logging.INFO) and InverterBase(reader, writer, 'tsun', Talent):
ProxyState.set_up(False)
app.testing = True
client = app.test_client()
response = await client.get('/-/healthy')
assert response.status_code == 200
result = await response.get_data()
assert result == b"I'm fine"
assert "" == caplog.text
ProxyState.set_up(True)
response = await client.get('/-/healthy')
assert response.status_code == 503
result = await response.get_data()
assert result == b"I have a problem"
assert "" == caplog.text
@pytest.mark.asyncio
async def test_healthy_exception(self, monkeypatch, caplog):
"""Test the healthy route."""
def result_except(self):
raise ValueError
LOGGER = logging.getLogger("mqtt")
LOGGER.propagate = True
LOGGER.setLevel(logging.INFO)
monkeypatch.setattr(InverterBase, "healthy", result_except)
InverterBase._registry.clear()
reader = FakeReader()
writer = FakeWriter()
with caplog.at_level(logging.INFO) and InverterBase(reader, writer, 'tsun', Talent):
ProxyState.set_up(False)
app.testing = True
client = app.test_client()
response = await client.get('/-/healthy')
assert response.status_code == 200
result = await response.get_data()
assert result == b"I'm fine"
assert "" == caplog.text
ProxyState.set_up(True)
response = await client.get('/-/healthy')
assert response.status_code == 200
result = await response.get_data()
assert result == b"I'm fine"
assert "Exception:" in caplog.text

View File

@@ -932,7 +932,7 @@ def config_tsun_dcu1():
Proxy.class_init() Proxy.class_init()
Proxy.mqtt = Mqtt() Proxy.mqtt = Mqtt()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_message(device_ind_msg): async def test_read_message(device_ind_msg):
Config.act_config = {'solarman':{'enabled': True}} Config.act_config = {'solarman':{'enabled': True}}
m = MemoryStream(device_ind_msg, (0,)) m = MemoryStream(device_ind_msg, (0,))
@@ -951,7 +951,7 @@ async def test_read_message(device_ind_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_start_byte(invalid_start_byte, device_ind_msg): async def test_invalid_start_byte(invalid_start_byte, device_ind_msg):
# received a message with wrong start byte plus an valid message # received a message with wrong start byte plus an valid message
# the complete receive buffer must be cleared to # the complete receive buffer must be cleared to
@@ -974,7 +974,7 @@ async def test_invalid_start_byte(invalid_start_byte, device_ind_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_stop_byte(invalid_stop_byte): async def test_invalid_stop_byte(invalid_stop_byte):
# received a message with wrong stop byte # received a message with wrong stop byte
# the complete receive buffer must be cleared to # the complete receive buffer must be cleared to
@@ -996,7 +996,7 @@ async def test_invalid_stop_byte(invalid_stop_byte):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg): async def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg):
# received a message with wrong stop byte plus an valid message # received a message with wrong stop byte plus an valid message
# only the first message must be discarded # only the first message must be discarded
@@ -1023,7 +1023,7 @@ async def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte): async def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte):
# received a message with wrong stop byte plus an invalid message # received a message with wrong stop byte plus an invalid message
# with fron start byte # with fron start byte
@@ -1047,7 +1047,7 @@ async def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_checksum(invalid_checksum, device_ind_msg): async def test_invalid_checksum(invalid_checksum, device_ind_msg):
# received a message with wrong checksum plus an valid message # received a message with wrong checksum plus an valid message
# only the first message must be discarded # only the first message must be discarded
@@ -1073,7 +1073,7 @@ async def test_invalid_checksum(invalid_checksum, device_ind_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rsp_msg): async def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rsp_msg):
_ = config_no_tsun_inv1 _ = config_no_tsun_inv1
m = MemoryStream(device_ind_msg, (0,)) m = MemoryStream(device_ind_msg, (0,))
@@ -1095,7 +1095,7 @@ async def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rs
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_message_in_chunks(device_ind_msg): async def test_read_message_in_chunks(device_ind_msg):
Config.act_config = {'solarman':{'enabled': True}} Config.act_config = {'solarman':{'enabled': True}}
m = MemoryStream(device_ind_msg, (4,11,0)) m = MemoryStream(device_ind_msg, (4,11,0))
@@ -1118,7 +1118,7 @@ async def test_read_message_in_chunks(device_ind_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_message_in_chunks2(my_loop, config_tsun_inv1, device_ind_msg): async def test_read_message_in_chunks2(my_loop, config_tsun_inv1, device_ind_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(device_ind_msg, (4,10,0)) m = MemoryStream(device_ind_msg, (4,10,0))
@@ -1144,7 +1144,7 @@ async def test_read_message_in_chunks2(my_loop, config_tsun_inv1, device_ind_msg
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_two_messages(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg): async def test_read_two_messages(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(device_ind_msg, (0,)) m = MemoryStream(device_ind_msg, (0,))
@@ -1173,7 +1173,7 @@ async def test_read_two_messages(my_loop, config_tsun_allow_all, device_ind_msg,
assert m.ifc.tx_fifo.get()==b'' assert m.ifc.tx_fifo.get()==b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_two_messages2(my_loop, config_tsun_allow_all, inverter_ind_msg, inverter_ind_msg_81, inverter_rsp_msg, inverter_rsp_msg_81): async def test_read_two_messages2(my_loop, config_tsun_allow_all, inverter_ind_msg, inverter_ind_msg_81, inverter_rsp_msg, inverter_rsp_msg_81):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg, (0,)) m = MemoryStream(inverter_ind_msg, (0,))
@@ -1199,7 +1199,7 @@ async def test_read_two_messages2(my_loop, config_tsun_allow_all, inverter_ind_m
assert m.ifc.tx_fifo.get()==b'' assert m.ifc.tx_fifo.get()==b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_two_messages3(my_loop, config_tsun_allow_all, device_ind_msg2, device_rsp_msg2, inverter_ind_msg, inverter_rsp_msg): async def test_read_two_messages3(my_loop, config_tsun_allow_all, device_ind_msg2, device_rsp_msg2, inverter_ind_msg, inverter_rsp_msg):
# test device message received after the inverter masg # test device message received after the inverter masg
_ = config_tsun_allow_all _ = config_tsun_allow_all
@@ -1229,7 +1229,7 @@ async def test_read_two_messages3(my_loop, config_tsun_allow_all, device_ind_msg
assert m.ifc.tx_fifo.get()==b'' assert m.ifc.tx_fifo.get()==b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_two_messages4(my_loop, config_tsun_dcu1, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg): async def test_read_two_messages4(my_loop, config_tsun_dcu1, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg):
_ = config_tsun_dcu1 _ = config_tsun_dcu1
m = MemoryStream(dcu_dev_ind_msg, (0,)) m = MemoryStream(dcu_dev_ind_msg, (0,))
@@ -1258,7 +1258,7 @@ async def test_read_two_messages4(my_loop, config_tsun_dcu1, dcu_dev_ind_msg, dc
assert m.ifc.tx_fifo.get()==b'' assert m.ifc.tx_fifo.get()==b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_unkown_frame_code(my_loop, config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81): async def test_unkown_frame_code(my_loop, config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(inverter_ind_msg_81, (0,)) m = MemoryStream(inverter_ind_msg_81, (0,))
@@ -1277,7 +1277,7 @@ async def test_unkown_frame_code(my_loop, config_tsun_inv1, inverter_ind_msg_81,
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_unkown_message(my_loop, config_tsun_inv1, unknown_msg): async def test_unkown_message(my_loop, config_tsun_inv1, unknown_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(unknown_msg, (0,)) m = MemoryStream(unknown_msg, (0,))
@@ -1296,7 +1296,7 @@ async def test_unkown_message(my_loop, config_tsun_inv1, unknown_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_device_rsp(my_loop, config_tsun_inv1, device_rsp_msg): async def test_device_rsp(my_loop, config_tsun_inv1, device_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(device_rsp_msg, (0,), False) m = MemoryStream(device_rsp_msg, (0,), False)
@@ -1315,7 +1315,7 @@ async def test_device_rsp(my_loop, config_tsun_inv1, device_rsp_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_inverter_rsp(my_loop, config_tsun_inv1, inverter_rsp_msg): async def test_inverter_rsp(my_loop, config_tsun_inv1, inverter_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(inverter_rsp_msg, (0,), False) m = MemoryStream(inverter_rsp_msg, (0,), False)
@@ -1334,7 +1334,7 @@ async def test_inverter_rsp(my_loop, config_tsun_inv1, inverter_rsp_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_heartbeat_ind(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): async def test_heartbeat_ind(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(heartbeat_ind_msg, (0,)) m = MemoryStream(heartbeat_ind_msg, (0,))
@@ -1352,7 +1352,7 @@ async def test_heartbeat_ind(my_loop, config_tsun_inv1, heartbeat_ind_msg, heart
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_heartbeat_ind2(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): async def test_heartbeat_ind2(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(heartbeat_ind_msg, (0,)) m = MemoryStream(heartbeat_ind_msg, (0,))
@@ -1371,7 +1371,7 @@ async def test_heartbeat_ind2(my_loop, config_tsun_inv1, heartbeat_ind_msg, hear
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_heartbeat_rsp(my_loop, config_tsun_inv1, heartbeat_rsp_msg): async def test_heartbeat_rsp(my_loop, config_tsun_inv1, heartbeat_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(heartbeat_rsp_msg, (0,), False) m = MemoryStream(heartbeat_rsp_msg, (0,), False)
@@ -1390,7 +1390,7 @@ async def test_heartbeat_rsp(my_loop, config_tsun_inv1, heartbeat_rsp_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_sync_start_ind(my_loop, config_tsun_inv1, sync_start_ind_msg, sync_start_rsp_msg, sync_start_fwd_msg): async def test_sync_start_ind(my_loop, config_tsun_inv1, sync_start_ind_msg, sync_start_rsp_msg, sync_start_fwd_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(sync_start_ind_msg, (0,)) m = MemoryStream(sync_start_ind_msg, (0,))
@@ -1414,7 +1414,7 @@ async def test_sync_start_ind(my_loop, config_tsun_inv1, sync_start_ind_msg, syn
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_sync_start_rsp(my_loop, config_tsun_inv1, sync_start_rsp_msg): async def test_sync_start_rsp(my_loop, config_tsun_inv1, sync_start_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(sync_start_rsp_msg, (0,), False) m = MemoryStream(sync_start_rsp_msg, (0,), False)
@@ -1433,7 +1433,7 @@ async def test_sync_start_rsp(my_loop, config_tsun_inv1, sync_start_rsp_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_sync_end_ind(my_loop, config_tsun_inv1, sync_end_ind_msg, sync_end_rsp_msg): async def test_sync_end_ind(my_loop, config_tsun_inv1, sync_end_ind_msg, sync_end_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(sync_end_ind_msg, (0,)) m = MemoryStream(sync_end_ind_msg, (0,))
@@ -1451,7 +1451,7 @@ async def test_sync_end_ind(my_loop, config_tsun_inv1, sync_end_ind_msg, sync_en
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_sync_end_rsp(my_loop, config_tsun_inv1, sync_end_rsp_msg): async def test_sync_end_rsp(my_loop, config_tsun_inv1, sync_end_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(sync_end_rsp_msg, (0,), False) m = MemoryStream(sync_end_rsp_msg, (0,), False)
@@ -1470,7 +1470,7 @@ async def test_sync_end_rsp(my_loop, config_tsun_inv1, sync_end_rsp_msg):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_600(my_loop, config_tsun_allow_all, inverter_ind_msg): async def test_build_modell_600(my_loop, config_tsun_allow_all, inverter_ind_msg):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg, (0,)) m = MemoryStream(inverter_ind_msg, (0,))
@@ -1491,7 +1491,7 @@ async def test_build_modell_600(my_loop, config_tsun_allow_all, inverter_ind_msg
assert m.ifc.tx_fifo.get()==b'' assert m.ifc.tx_fifo.get()==b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_1600(my_loop, config_tsun_allow_all, inverter_ind_msg1600): async def test_build_modell_1600(my_loop, config_tsun_allow_all, inverter_ind_msg1600):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg1600, (0,)) m = MemoryStream(inverter_ind_msg1600, (0,))
@@ -1505,7 +1505,7 @@ async def test_build_modell_1600(my_loop, config_tsun_allow_all, inverter_ind_ms
assert 'TSOL-MS1600' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) assert 'TSOL-MS1600' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_1800(my_loop, config_tsun_allow_all, inverter_ind_msg1800): async def test_build_modell_1800(my_loop, config_tsun_allow_all, inverter_ind_msg1800):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg1800, (0,)) m = MemoryStream(inverter_ind_msg1800, (0,))
@@ -1519,7 +1519,7 @@ async def test_build_modell_1800(my_loop, config_tsun_allow_all, inverter_ind_ms
assert 'TSOL-MS1800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) assert 'TSOL-MS1800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_2000(my_loop, config_tsun_allow_all, inverter_ind_msg2000): async def test_build_modell_2000(my_loop, config_tsun_allow_all, inverter_ind_msg2000):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg2000, (0,)) m = MemoryStream(inverter_ind_msg2000, (0,))
@@ -1533,7 +1533,7 @@ async def test_build_modell_2000(my_loop, config_tsun_allow_all, inverter_ind_ms
assert 'TSOL-MS2000' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) assert 'TSOL-MS2000' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_800(my_loop, config_tsun_allow_all, inverter_ind_msg800): async def test_build_modell_800(my_loop, config_tsun_allow_all, inverter_ind_msg800):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg800, (0,)) m = MemoryStream(inverter_ind_msg800, (0,))
@@ -1547,7 +1547,7 @@ async def test_build_modell_800(my_loop, config_tsun_allow_all, inverter_ind_msg
assert 'TSOL-MS800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) assert 'TSOL-MS800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_modell_900(my_loop, config_tsun_allow_all, inverter_ind_msg900): async def test_build_modell_900(my_loop, config_tsun_allow_all, inverter_ind_msg900):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(inverter_ind_msg900, (0,)) m = MemoryStream(inverter_ind_msg900, (0,))
@@ -1561,7 +1561,7 @@ async def test_build_modell_900(my_loop, config_tsun_allow_all, inverter_ind_msg
assert 'TSOL-MSxx00' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0) assert 'TSOL-MSxx00' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_build_logger_modell(my_loop, config_tsun_allow_all, device_ind_msg): async def test_build_logger_modell(my_loop, config_tsun_allow_all, device_ind_msg):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(device_ind_msg, (0,)) m = MemoryStream(device_ind_msg, (0,))
@@ -1573,7 +1573,7 @@ async def test_build_logger_modell(my_loop, config_tsun_allow_all, device_ind_ms
assert 'V1.1.00.0B' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00') assert 'V1.1.00.0B' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00')
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_iterator(my_loop, config_tsun_inv1): async def test_msg_iterator(my_loop, config_tsun_inv1):
Message._registry.clear() Message._registry.clear()
m1 = SolarmanV5(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) m1 = SolarmanV5(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
@@ -1595,7 +1595,7 @@ async def test_msg_iterator(my_loop, config_tsun_inv1):
assert test1 == 1 assert test1 == 1
assert test2 == 1 assert test2 == 1
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_proxy_counter(my_loop, config_tsun_inv1): async def test_proxy_counter(my_loop, config_tsun_inv1):
m = SolarmanV5(None, ('test.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False) m = SolarmanV5(None, ('test.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
assert m.new_data == {} assert m.new_data == {}
@@ -1614,7 +1614,7 @@ async def test_proxy_counter(my_loop, config_tsun_inv1):
assert 0 == m.db.stat['proxy']['Unknown_Msg'] assert 0 == m.db.stat['proxy']['Unknown_Msg']
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_build_modbus_req(my_loop, config_tsun_inv1, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, msg_modbus_cmd): async def test_msg_build_modbus_req(my_loop, config_tsun_inv1, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, msg_modbus_cmd):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(device_ind_msg, (0,), True) m = MemoryStream(device_ind_msg, (0,), True)
@@ -1624,7 +1624,7 @@ async def test_msg_build_modbus_req(my_loop, config_tsun_inv1, device_ind_msg, d
assert m.ifc.tx_fifo.get()==device_rsp_msg assert m.ifc.tx_fifo.get()==device_rsp_msg
assert m.ifc.fwd_fifo.get()==device_ind_msg assert m.ifc.fwd_fifo.get()==device_ind_msg
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
assert 0 == m.send_msg_ofs assert 0 == m.send_msg_ofs
assert m.ifc.fwd_fifo.get() == b'' assert m.ifc.fwd_fifo.get() == b''
assert m.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up assert m.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up
@@ -1642,14 +1642,14 @@ async def test_msg_build_modbus_req(my_loop, config_tsun_inv1, device_ind_msg, d
assert m.ifc.tx_fifo.get()==inverter_rsp_msg assert m.ifc.tx_fifo.get()==inverter_rsp_msg
assert m.ifc.fwd_fifo.get()==inverter_ind_msg assert m.ifc.fwd_fifo.get()==inverter_ind_msg
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
assert 0 == m.send_msg_ofs assert 0 == m.send_msg_ofs
assert m.ifc.fwd_fifo.get() == b'' assert m.ifc.fwd_fifo.get() == b''
assert m.sent_pdu == msg_modbus_cmd assert m.sent_pdu == msg_modbus_cmd
assert m.ifc.tx_fifo.get()== b'' assert m.ifc.tx_fifo.get()== b''
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_at_cmd(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg, at_command_rsp_msg): async def test_at_cmd(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg, at_command_rsp_msg):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(device_ind_msg, (0,), True) m = MemoryStream(device_ind_msg, (0,), True)
@@ -1709,7 +1709,7 @@ async def test_at_cmd(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp
assert Proxy.mqtt.data == "" assert Proxy.mqtt.data == ""
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_at_cmd_blocked(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg): async def test_at_cmd_blocked(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg):
_ = config_tsun_allow_all _ = config_tsun_allow_all
m = MemoryStream(device_ind_msg, (0,), True) m = MemoryStream(device_ind_msg, (0,), True)
@@ -1744,7 +1744,7 @@ async def test_at_cmd_blocked(my_loop, config_tsun_allow_all, device_ind_msg, de
assert Proxy.mqtt.data == "'AT+WEBU' is forbidden" assert Proxy.mqtt.data == "'AT+WEBU' is forbidden"
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_at_cmd_ind(my_loop, config_tsun_inv1, at_command_ind_msg, at_command_rsp_msg): async def test_at_cmd_ind(my_loop, config_tsun_inv1, at_command_ind_msg, at_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(at_command_ind_msg, (0,), False) m = MemoryStream(at_command_ind_msg, (0,), False)
@@ -1780,7 +1780,7 @@ async def test_at_cmd_ind(my_loop, config_tsun_inv1, at_command_ind_msg, at_comm
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_at_cmd_ind_block(my_loop, config_tsun_inv1, at_command_ind_msg_block): async def test_at_cmd_ind_block(my_loop, config_tsun_inv1, at_command_ind_msg_block):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(at_command_ind_msg_block, (0,), False) m = MemoryStream(at_command_ind_msg_block, (0,), False)
@@ -1809,7 +1809,7 @@ async def test_at_cmd_ind_block(my_loop, config_tsun_inv1, at_command_ind_msg_bl
assert Proxy.mqtt.data == "" assert Proxy.mqtt.data == ""
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_at_command_rsp1(my_loop, config_tsun_inv1, at_command_rsp_msg): async def test_msg_at_command_rsp1(my_loop, config_tsun_inv1, at_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(at_command_rsp_msg) m = MemoryStream(at_command_rsp_msg)
@@ -1829,7 +1829,7 @@ async def test_msg_at_command_rsp1(my_loop, config_tsun_inv1, at_command_rsp_msg
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_at_command_rsp2(my_loop, config_tsun_inv1, at_command_rsp_msg): async def test_msg_at_command_rsp2(my_loop, config_tsun_inv1, at_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(at_command_rsp_msg) m = MemoryStream(at_command_rsp_msg)
@@ -1851,7 +1851,7 @@ async def test_msg_at_command_rsp2(my_loop, config_tsun_inv1, at_command_rsp_msg
assert Proxy.mqtt.data == "+ok" assert Proxy.mqtt.data == "+ok"
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_at_command_rsp3(my_loop, config_tsun_inv1, at_command_interim_rsp_msg): async def test_msg_at_command_rsp3(my_loop, config_tsun_inv1, at_command_interim_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(at_command_interim_rsp_msg) m = MemoryStream(at_command_interim_rsp_msg)
@@ -1877,7 +1877,7 @@ async def test_msg_at_command_rsp3(my_loop, config_tsun_inv1, at_command_interim
assert Proxy.mqtt.data == "" assert Proxy.mqtt.data == ""
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_req(my_loop, config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd): async def test_msg_modbus_req(my_loop, config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(b'') m = MemoryStream(b'')
@@ -1906,7 +1906,7 @@ async def test_msg_modbus_req(my_loop, config_tsun_inv1, msg_modbus_cmd, msg_mod
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_req_seq(my_loop, config_tsun_inv1, msg_modbus_cmd_seq): async def test_msg_modbus_req_seq(my_loop, config_tsun_inv1, msg_modbus_cmd_seq):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(b'') m = MemoryStream(b'')
@@ -1935,7 +1935,7 @@ async def test_msg_modbus_req_seq(my_loop, config_tsun_inv1, msg_modbus_cmd_seq)
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_req2(my_loop, config_tsun_inv1, msg_modbus_cmd_crc_err): async def test_msg_modbus_req2(my_loop, config_tsun_inv1, msg_modbus_cmd_crc_err):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(b'') m = MemoryStream(b'')
@@ -1963,7 +1963,7 @@ async def test_msg_modbus_req2(my_loop, config_tsun_inv1, msg_modbus_cmd_crc_err
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_unknown_cmd_req(my_loop, config_tsun_inv1, msg_unknown_cmd): async def test_msg_unknown_cmd_req(my_loop, config_tsun_inv1, msg_unknown_cmd):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(msg_unknown_cmd, (0,), False) m = MemoryStream(msg_unknown_cmd, (0,), False)
@@ -1986,7 +1986,7 @@ async def test_msg_unknown_cmd_req(my_loop, config_tsun_inv1, msg_unknown_cmd):
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_rsp1(my_loop, config_tsun_inv1, msg_modbus_rsp): async def test_msg_modbus_rsp1(my_loop, config_tsun_inv1, msg_modbus_rsp):
'''Modbus response without a valid Modbus request must be dropped''' '''Modbus response without a valid Modbus request must be dropped'''
_ = config_tsun_inv1 _ = config_tsun_inv1
@@ -2006,7 +2006,7 @@ async def test_msg_modbus_rsp1(my_loop, config_tsun_inv1, msg_modbus_rsp):
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_rsp2(my_loop, config_tsun_inv1, msg_modbus_rsp): async def test_msg_modbus_rsp2(my_loop, config_tsun_inv1, msg_modbus_rsp):
'''Modbus response with a valid Modbus request must be forwarded''' '''Modbus response with a valid Modbus request must be forwarded'''
_ = config_tsun_inv1 # setup config structure _ = config_tsun_inv1 # setup config structure
@@ -2044,7 +2044,7 @@ async def test_msg_modbus_rsp2(my_loop, config_tsun_inv1, msg_modbus_rsp):
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_rsp3(my_loop, config_tsun_inv1, msg_modbus_rsp): async def test_msg_modbus_rsp3(my_loop, config_tsun_inv1, msg_modbus_rsp):
'''Modbus response with a valid Modbus request must be forwarded''' '''Modbus response with a valid Modbus request must be forwarded'''
_ = config_tsun_inv1 _ = config_tsun_inv1
@@ -2081,7 +2081,7 @@ async def test_msg_modbus_rsp3(my_loop, config_tsun_inv1, msg_modbus_rsp):
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_unknown_rsp(my_loop, config_tsun_inv1, msg_unknown_cmd_rsp): async def test_msg_unknown_rsp(my_loop, config_tsun_inv1, msg_unknown_cmd_rsp):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(msg_unknown_cmd_rsp) m = MemoryStream(msg_unknown_cmd_rsp)
@@ -2100,7 +2100,7 @@ async def test_msg_unknown_rsp(my_loop, config_tsun_inv1, msg_unknown_cmd_rsp):
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_invalid(my_loop, config_tsun_inv1, msg_modbus_invalid): async def test_msg_modbus_invalid(my_loop, config_tsun_inv1, msg_modbus_invalid):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(msg_modbus_invalid, (0,), False) m = MemoryStream(msg_modbus_invalid, (0,), False)
@@ -2115,7 +2115,7 @@ async def test_msg_modbus_invalid(my_loop, config_tsun_inv1, msg_modbus_invalid)
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_modbus_fragment(my_loop, config_tsun_inv1, msg_modbus_rsp): async def test_msg_modbus_fragment(my_loop, config_tsun_inv1, msg_modbus_rsp):
_ = config_tsun_inv1 _ = config_tsun_inv1
# receive more bytes than expected (7 bytes from the next msg) # receive more bytes than expected (7 bytes from the next msg)
@@ -2141,7 +2141,7 @@ async def test_msg_modbus_fragment(my_loop, config_tsun_inv1, msg_modbus_rsp):
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_polling(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): async def test_modbus_polling(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2181,7 +2181,7 @@ async def test_modbus_polling(my_loop, config_tsun_inv1, heartbeat_ind_msg, hear
assert next(m.mb_timer.exp_count) == 4 assert next(m.mb_timer.exp_count) == 4
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_scaning(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp, msg_modbus_rsp_inv_id2): async def test_modbus_scaning(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp, msg_modbus_rsp_inv_id2):
_ = config_tsun_scan _ = config_tsun_scan
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2254,7 +2254,7 @@ async def test_modbus_scaning(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp
assert next(m.mb_timer.exp_count) == 3 assert next(m.mb_timer.exp_count) == 3
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_scaning_inv_rsp(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp_mb_4): async def test_modbus_scaning_inv_rsp(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp_mb_4):
_ = config_tsun_scan _ = config_tsun_scan
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2309,7 +2309,7 @@ async def test_modbus_scaning_inv_rsp(config_tsun_scan, heartbeat_ind_msg, heart
assert next(m.mb_timer.exp_count) == 2 assert next(m.mb_timer.exp_count) == 2
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip): async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2318,7 +2318,7 @@ async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip):
assert m.no_forwarding == False assert m.no_forwarding == False
assert m.mb_timer.tim == None assert m.mb_timer.tim == None
assert asyncio.get_running_loop() == m.mb_timer.loop assert asyncio.get_running_loop() == m.mb_timer.loop
await m.send_start_cmd(get_sn_int(), str_test_ip, False, m.mb_first_timeout) m.send_start_cmd(get_sn_int(), str_test_ip, False, m.mb_first_timeout)
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x01\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf1\x15') assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x01\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf1\x15')
assert m.db.get_db_value(Register.IP_ADDRESS) == str_test_ip assert m.db.get_db_value(Register.IP_ADDRESS) == str_test_ip
assert isclose(m.db.get_db_value(Register.POLLING_INTERVAL), 0.5) assert isclose(m.db.get_db_value(Register.POLLING_INTERVAL), 0.5)
@@ -2341,7 +2341,7 @@ async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip):
assert next(m.mb_timer.exp_count) == 3 assert next(m.mb_timer.exp_count) == 3
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_start_client_mode_scan(config_tsun_scan_dcu, str_test_ip, dcu_modbus_rsp): async def test_start_client_mode_scan(config_tsun_scan_dcu, str_test_ip, dcu_modbus_rsp):
_ = config_tsun_scan_dcu _ = config_tsun_scan_dcu
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2351,7 +2351,7 @@ async def test_start_client_mode_scan(config_tsun_scan_dcu, str_test_ip, dcu_mod
assert m.no_forwarding == False assert m.no_forwarding == False
assert m.mb_timer.tim == None assert m.mb_timer.tim == None
assert asyncio.get_running_loop() == m.mb_timer.loop assert asyncio.get_running_loop() == m.mb_timer.loop
await m.send_start_cmd(get_dcu_sn_int(), str_test_ip, False, m.mb_first_timeout) m.send_start_cmd(get_dcu_sn_int(), str_test_ip, False, m.mb_first_timeout)
assert m.mb_start_reg == 0x0000 assert m.mb_start_reg == 0x0000
assert m.mb_step == 0x100 assert m.mb_step == 0x100
assert m.mb_bytes == 0x2d assert m.mb_bytes == 0x2d
@@ -2414,7 +2414,7 @@ async def test_start_client_mode_scan(config_tsun_scan_dcu, str_test_ip, dcu_mod
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_timeout(my_loop, config_tsun_inv1): async def test_timeout(my_loop, config_tsun_inv1):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(b'') m = MemoryStream(b'')
@@ -2428,7 +2428,7 @@ async def test_timeout(my_loop, config_tsun_inv1):
m.state = State.closed m.state = State.closed
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_fnc_dispatch(my_loop, config_tsun_inv1): async def test_fnc_dispatch(my_loop, config_tsun_inv1):
def msg(): def msg():
return return
@@ -2450,7 +2450,7 @@ async def test_fnc_dispatch(my_loop, config_tsun_inv1):
assert _obj == m.msg_unknown assert _obj == m.msg_unknown
assert _str == "'msg_unknown'" assert _str == "'msg_unknown'"
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_timestamp(my_loop, config_tsun_inv1): async def test_timestamp(my_loop, config_tsun_inv1):
m = MemoryStream(b'') m = MemoryStream(b'')
ts = m._timestamp() ts = m._timestamp()
@@ -2477,7 +2477,7 @@ class InverterTest(InverterBase):
dst.ifc.tx_add(src.ifc.fwd_fifo.get()) dst.ifc.tx_add(src.ifc.fwd_fifo.get())
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_proxy_at_cmd(my_loop, config_tsun_inv1, patch_open_connection, at_command_ind_msg, at_command_rsp_msg): async def test_proxy_at_cmd(my_loop, config_tsun_inv1, patch_open_connection, at_command_ind_msg, at_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
_ = patch_open_connection _ = patch_open_connection
@@ -2515,7 +2515,7 @@ async def test_proxy_at_cmd(my_loop, config_tsun_inv1, patch_open_connection, at
assert Proxy.mqtt.key == '' assert Proxy.mqtt.key == ''
assert Proxy.mqtt.data == "" assert Proxy.mqtt.data == ""
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_proxy_at_blocked(my_loop, config_tsun_inv1, patch_open_connection, at_command_ind_msg_block, at_command_rsp_msg): async def test_proxy_at_blocked(my_loop, config_tsun_inv1, patch_open_connection, at_command_ind_msg_block, at_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
_ = patch_open_connection _ = patch_open_connection
@@ -2553,7 +2553,7 @@ async def test_proxy_at_blocked(my_loop, config_tsun_inv1, patch_open_connection
assert Proxy.mqtt.key == 'tsun/inv1/at_resp' assert Proxy.mqtt.key == 'tsun/inv1/at_resp'
assert Proxy.mqtt.data == "+ok" assert Proxy.mqtt.data == "+ok"
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_dcu_cmd(my_loop, config_tsun_allow_all, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg, dcu_command_ind_msg, dcu_command_rsp_msg): async def test_dcu_cmd(my_loop, config_tsun_allow_all, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg, dcu_command_ind_msg, dcu_command_rsp_msg):
'''test dcu_power command fpr a DCU device with sensor 0x3026''' '''test dcu_power command fpr a DCU device with sensor 0x3026'''
_ = config_tsun_allow_all _ = config_tsun_allow_all
@@ -2600,7 +2600,7 @@ async def test_dcu_cmd(my_loop, config_tsun_allow_all, dcu_dev_ind_msg, dcu_dev_
assert Proxy.mqtt.data == "+ok" assert Proxy.mqtt.data == "+ok"
Proxy.mqtt.clear() # clear last test result Proxy.mqtt.clear() # clear last test result
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_dcu_cmd_not_supported(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg): async def test_dcu_cmd_not_supported(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg):
'''test that an inverter don't accept the dcu_power command''' '''test that an inverter don't accept the dcu_power command'''
_ = config_tsun_allow_all _ = config_tsun_allow_all
@@ -2632,7 +2632,7 @@ async def test_dcu_cmd_not_supported(my_loop, config_tsun_allow_all, device_ind_
assert m.sent_pdu == b'' assert m.sent_pdu == b''
Proxy.mqtt.clear() # clear last test result Proxy.mqtt.clear() # clear last test result
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_proxy_dcu_cmd(my_loop, config_tsun_dcu1, patch_open_connection, dcu_command_ind_msg, dcu_command_rsp_msg): async def test_proxy_dcu_cmd(my_loop, config_tsun_dcu1, patch_open_connection, dcu_command_ind_msg, dcu_command_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
_ = patch_open_connection _ = patch_open_connection

View File

@@ -128,7 +128,7 @@ def heartbeat_ind():
msg = b'\xa5\x01\x00\x10G\x00\x01\x00\x00\x00\x00\x00Y\x15' msg = b'\xa5\x01\x00\x10G\x00\x01\x00\x00\x00\x00\x00Y\x15'
return msg return msg
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_emu_init_close(my_loop, config_tsun_inv1): async def test_emu_init_close(my_loop, config_tsun_inv1):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -137,14 +137,14 @@ async def test_emu_init_close(my_loop, config_tsun_inv1):
cld.close() cld.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_emu_start(my_loop, config_tsun_inv1, msg_modbus_rsp, str_test_ip, device_ind_msg): async def test_emu_start(my_loop, config_tsun_inv1, msg_modbus_rsp, str_test_ip, device_ind_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
inv = InvStream(msg_modbus_rsp) inv = InvStream(msg_modbus_rsp)
assert asyncio.get_running_loop() == inv.mb_timer.loop assert asyncio.get_running_loop() == inv.mb_timer.loop
await inv.send_start_cmd(get_sn_int(), str_test_ip, True, inv.mb_first_timeout) inv.send_start_cmd(get_sn_int(), str_test_ip, True, inv.mb_first_timeout)
inv.read() # read complete msg, and dispatch msg inv.read() # read complete msg, and dispatch msg
assert not inv.header_valid # must be invalid, since msg was handled and buffer flushed assert not inv.header_valid # must be invalid, since msg was handled and buffer flushed
assert inv.msg_count == 1 assert inv.msg_count == 1
@@ -155,18 +155,18 @@ async def test_emu_start(my_loop, config_tsun_inv1, msg_modbus_rsp, str_test_ip,
assert inv.ifc.fwd_fifo.peek() == device_ind_msg assert inv.ifc.fwd_fifo.peek() == device_ind_msg
cld.close() cld.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_snd_hb(my_loop, config_tsun_inv1, heartbeat_ind): async def test_snd_hb(my_loop, config_tsun_inv1, heartbeat_ind):
_ = config_tsun_inv1 _ = config_tsun_inv1
inv = InvStream() inv = InvStream()
cld = CldStream(inv) cld = CldStream(inv)
# await inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout) # inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout)
cld.send_heartbeat_cb(0) cld.send_heartbeat_cb(0)
assert cld.ifc.tx_fifo.peek() == heartbeat_ind assert cld.ifc.tx_fifo.peek() == heartbeat_ind
cld.close() cld.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_snd_inv_data(my_loop, config_tsun_inv1, inverter_ind_msg, inverter_rsp_msg): async def test_snd_inv_data(my_loop, config_tsun_inv1, inverter_ind_msg, inverter_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
inv = InvStream() inv = InvStream()
@@ -178,7 +178,7 @@ async def test_snd_inv_data(my_loop, config_tsun_inv1, inverter_ind_msg, inverte
inv.db.set_db_def_value(Register.GRID_FREQUENCY, 50.05) inv.db.set_db_def_value(Register.GRID_FREQUENCY, 50.05)
inv.db.set_db_def_value(Register.PROD_COMPL_TYPE, 6) inv.db.set_db_def_value(Register.PROD_COMPL_TYPE, 6)
assert asyncio.get_running_loop() == inv.mb_timer.loop assert asyncio.get_running_loop() == inv.mb_timer.loop
await inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout) inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout)
inv.db.set_db_def_value(Register.DATA_UP_INTERVAL, 17) # set test value inv.db.set_db_def_value(Register.DATA_UP_INTERVAL, 17) # set test value
cld = CldStream(inv) cld = CldStream(inv)
@@ -208,12 +208,12 @@ async def test_snd_inv_data(my_loop, config_tsun_inv1, inverter_ind_msg, inverte
cld.close() cld.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_rcv_invalid(my_loop, config_tsun_inv1, inverter_ind_msg, inverter_rsp_msg): async def test_rcv_invalid(my_loop, config_tsun_inv1, inverter_ind_msg, inverter_rsp_msg):
_ = config_tsun_inv1 _ = config_tsun_inv1
inv = InvStream() inv = InvStream()
assert asyncio.get_running_loop() == inv.mb_timer.loop assert asyncio.get_running_loop() == inv.mb_timer.loop
await inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout) inv.send_start_cmd(get_sn_int(), str_test_ip, False, inv.mb_first_timeout)
inv.db.set_db_def_value(Register.DATA_UP_INTERVAL, 17) # set test value inv.db.set_db_def_value(Register.DATA_UP_INTERVAL, 17) # set test value
cld = CldStream(inv) cld = CldStream(inv)

View File

@@ -1048,7 +1048,7 @@ def msg_inverter_ms3000_ind(): # Data indication from the controller
msg += b'\x53\x00\x66' # | S.f' msg += b'\x53\x00\x66' # | S.f'
return msg return msg
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_read_message(msg_contact_info): async def test_read_message(msg_contact_info):
Config.act_config = {'tsun':{'enabled': True}} Config.act_config = {'tsun':{'enabled': True}}
m = MemoryStream(msg_contact_info, (0,)) m = MemoryStream(msg_contact_info, (0,))
@@ -2406,19 +2406,19 @@ def test_msg_modbus_fragment(config_tsun_inv1, msg_modbus_rsp20):
assert m.db.stat['proxy']['Modbus_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_msg_build_modbus_req(config_tsun_inv1, msg_modbus_cmd): async def test_msg_build_modbus_req(config_tsun_inv1, msg_modbus_cmd):
_ = config_tsun_inv1 _ = config_tsun_inv1
m = MemoryStream(b'', (0,), True) m = MemoryStream(b'', (0,), True)
m.id_str = b"R170000000000001" m.id_str = b"R170000000000001"
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
assert 0 == m.send_msg_ofs assert 0 == m.send_msg_ofs
assert m.ifc.fwd_fifo.get() == b'' assert m.ifc.fwd_fifo.get() == b''
assert m.ifc.tx_fifo.get() == b'' assert m.ifc.tx_fifo.get() == b''
assert m.sent_pdu == b'' assert m.sent_pdu == b''
m.state = State.up m.state = State.up
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
assert 0 == m.send_msg_ofs assert 0 == m.send_msg_ofs
assert m.ifc.fwd_fifo.get() == b'' assert m.ifc.fwd_fifo.get() == b''
assert m.ifc.tx_fifo.get() == b'' assert m.ifc.tx_fifo.get() == b''
@@ -2445,7 +2445,7 @@ def test_modbus_no_polling(config_no_modbus_poll, msg_get_time):
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_polling(config_tsun_inv1, msg_inverter_ind): async def test_modbus_polling(config_tsun_inv1, msg_inverter_ind):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()
@@ -2486,7 +2486,7 @@ async def test_modbus_polling(config_tsun_inv1, msg_inverter_ind):
assert next(m.mb_timer.exp_count) == 4 assert next(m.mb_timer.exp_count) == 4
m.close() m.close()
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_modbus_scaning(config_tsun_inv1, msg_inverter_ind, msg_modbus_rsp21): async def test_modbus_scaning(config_tsun_inv1, msg_inverter_ind, msg_modbus_rsp21):
_ = config_tsun_inv1 _ = config_tsun_inv1
assert asyncio.get_running_loop() assert asyncio.get_running_loop()

View File

@@ -1,22 +1,37 @@
# test_with_pytest.py # test_with_pytest.py
import pytest import pytest
from server import app import logging
from web import Web, web import os, errno
import datetime
from os import DirEntry, stat_result
from quart import current_app
from mock import patch
from server import app as my_app
from server import Server
from web import web
from async_stream import AsyncStreamClient from async_stream import AsyncStreamClient
from gen3plus.inverter_g3p import InverterG3P from gen3plus.inverter_g3p import InverterG3P
from web.log_handler import LogHandler
from test_inverter_g3p import FakeReader, FakeWriter, config_conn from test_inverter_g3p import FakeReader, FakeWriter, config_conn
from cnf.config import Config from cnf.config import Config
from mock import patch
from proxy import Proxy from proxy import Proxy
import os, errno
from os import DirEntry, stat_result
import datetime class FakeServer(Server):
def __init__(self):
pass # don't call the suoer(.__init__ for unit tests
pytest_plugins = ('pytest_asyncio',) pytest_plugins = ('pytest_asyncio',)
@pytest.fixture(scope="session")
def app():
yield my_app
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def client(): def client(app):
app.secret_key = 'super secret key' app.secret_key = 'super secret key'
app.testing = True
return app.test_client() return app.test_client()
@pytest.fixture @pytest.fixture
@@ -46,112 +61,118 @@ def create_inverter_client(config_conn):
return inv return inv
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_home(client): async def test_home(client):
"""Test the home route.""" """Test the home route."""
response = await client.get('/') response = await client.get('/')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b"<title>TSUN Proxy - Connections</title>" in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_page(client): async def test_page(client):
"""Test the mqtt page route.""" """Test the mqtt page route."""
response = await client.get('/mqtt') response = await client.get('/mqtt')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b"<title>TSUN Proxy - MQTT Status</title>" in await response.data
assert b'fetch("/mqtt-fetch")' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_rel_page(client): async def test_rel_page(client):
"""Test the mqtt route.""" """Test the mqtt route with relative paths."""
web.build_relative_urls = True web.build_relative_urls = True
response = await client.get('/mqtt') response = await client.get('/mqtt')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b'fetch("./mqtt-fetch")' in await response.data
web.build_relative_urls = False web.build_relative_urls = False
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_notes(client): async def test_notes(client):
"""Test the notes page route.""" """Test the notes page route."""
response = await client.get('/notes') response = await client.get('/notes')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b"<title>TSUN Proxy - Important Messages</title>" in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_logging(client): async def test_logging(client):
"""Test the logging page route.""" """Test the logging page route."""
response = await client.get('/logging') response = await client.get('/logging')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b"<title>TSUN Proxy - Log Files</title>" in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_favicon96(client): async def test_favicon96(client):
"""Test the favicon-96x96.png route.""" """Test the favicon-96x96.png route."""
response = await client.get('/favicon-96x96.png') response = await client.get('/favicon-96x96.png')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'image/png' assert response.mimetype == 'image/png'
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_favicon(client): async def test_favicon(client):
"""Test the favicon.ico route.""" """Test the favicon.ico route."""
response = await client.get('/favicon.ico') response = await client.get('/favicon.ico')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'image/x-icon' assert response.mimetype == 'image/x-icon'
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_favicon_svg(client): async def test_favicon_svg(client):
"""Test the favicon.svg route.""" """Test the favicon.svg route."""
response = await client.get('/favicon.svg') response = await client.get('/favicon.svg')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'image/svg+xml' assert response.mimetype == 'image/svg+xml'
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_apple_touch_icon(client): async def test_apple_touch_icon(client):
"""Test the apple-touch-icon.png route.""" """Test the apple-touch-icon.png route."""
response = await client.get('/apple-touch-icon.png') response = await client.get('/apple-touch-icon.png')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'image/png' assert response.mimetype == 'image/png'
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_manifest(client): async def test_manifest(client):
"""Test the site.webmanifest route.""" """Test the site.webmanifest route."""
response = await client.get('/site.webmanifest') response = await client.get('/site.webmanifest')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'application/manifest+json' assert response.mimetype == 'application/manifest+json'
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_data_fetch(create_inverter): async def test_data_fetch(client, create_inverter):
"""Test the data-fetch route.""" """Test the data-fetch route."""
_ = create_inverter _ = create_inverter
client = app.test_client()
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h5>Connections</h5>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_data_fetch1(create_inverter_server): async def test_data_fetch1(client, create_inverter_server):
"""Test the data-fetch route with server connection.""" """Test the data-fetch route with server connection."""
_ = create_inverter_server _ = create_inverter_server
client = app.test_client()
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h5>Connections</h5>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_data_fetch2(create_inverter_client): async def test_data_fetch2(client, create_inverter_client):
"""Test the data-fetch route with client connection.""" """Test the data-fetch route with client connection."""
_ = create_inverter_client _ = create_inverter_client
client = app.test_client()
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
response = await client.get('/data-fetch') response = await client.get('/data-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h5>Connections</h5>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_language_en(client): async def test_language_en(client):
"""Test the language/en route and cookie.""" """Test the language/en route and cookie."""
response = await client.get('/language/en', headers={'referer': '/index'}) response = await client.get('/language/en', headers={'referer': '/index'})
@@ -159,31 +180,60 @@ async def test_language_en(client):
assert response.content_language.pop() == 'en' assert response.content_language.pop() == 'en'
assert response.location == '/index' assert response.location == '/index'
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b'<html lang=en' in await response.data
assert b'<title>Redirecting...</title>' in await response.data
client.set_cookie('test', key='language', value='de') client.set_cookie('test', key='language', value='de')
response = await client.get('/mqtt') response = await client.get('/')
assert response.status_code == 200 assert response.status_code == 200
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b'<html lang="en"' in await response.data
assert b'<title>TSUN Proxy - Connections</title>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_language_de(client): async def test_language_de(client):
"""Test the language/de route.""" """Test the language/de route."""
response = await client.get('/language/de', headers={'referer': '/'}) response = await client.get('/language/de', headers={'referer': '/'})
assert response.status_code == 302 assert response.status_code == 302
assert response.content_language.pop() == 'de' assert response.content_language.pop() == 'de'
assert response.location == '/' assert response.location == '/'
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
assert b'<html lang=en>' in await response.data
assert b'<title>Redirecting...</title>' in await response.data
client.set_cookie('test', key='language', value='en')
response = await client.get('/')
assert response.status_code == 200
assert response.mimetype == 'text/html'
assert b'<html lang="de"' in await response.data
# the following assert fails on github runner, since the translation to german fails
# assert b'<title>TSUN Proxy - Verbindungen</title>' in await response.data
@pytest.mark.asyncio """Switch back to english"""
response = await client.get('/language/en', headers={'referer': '/index'})
assert response.status_code == 302
assert response.content_language.pop() == 'en'
assert response.location == '/index'
assert response.mimetype == 'text/html'
assert b'<html lang=en>' in await response.data
assert b'<title>Redirecting...</title>' in await response.data
@pytest.mark.asyncio(loop_scope="session")
async def test_language_unknown(client): async def test_language_unknown(client):
"""Test the language/unknown route.""" """Test the language/unknown route."""
response = await client.get('/language/unknown') response = await client.get('/language/unknown')
assert response.status_code == 404 assert response.status_code == 404
assert response.mimetype == 'text/html' assert response.mimetype == 'text/html'
client.set_cookie('test', key='language', value='en')
response = await client.get('/')
assert response.status_code == 200
assert response.mimetype == 'text/html'
assert b'<title>TSUN Proxy - Connections</title>' in await response.data
@pytest.mark.asyncio
@pytest.mark.asyncio(loop_scope="session")
async def test_mqtt_fetch(client, create_inverter): async def test_mqtt_fetch(client, create_inverter):
"""Test the mqtt-fetch route.""" """Test the mqtt-fetch route."""
_ = create_inverter _ = create_inverter
@@ -191,18 +241,50 @@ async def test_mqtt_fetch(client, create_inverter):
response = await client.get('/mqtt-fetch') response = await client.get('/mqtt-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h5>MQTT devices</h5>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_notes_fetch(client, config_conn): async def test_notes_fetch(client, config_conn):
"""Test the notes-fetch route.""" """Test the notes-fetch route."""
_ = create_inverter _ = config_conn
s = FakeServer()
s.src_dir = 'app/src/'
s.init_logging_system()
# First clear log and test Well done message
logh = LogHandler()
logh.clear()
response = await client.get('/notes-fetch') response = await client.get('/notes-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h2>Well done!</h2>' in await response.data
# Check info logs which must be ignored here
logging.info('config_info')
logh.flush()
response = await client.get('/notes-fetch')
assert response.status_code == 200
assert b'<h2>Well done!</h2>' in await response.data
# Check warning logs which must be added to the note list
logging.warning('config_warning')
logh.flush()
response = await client.get('/notes-fetch')
assert response.status_code == 200
assert b'WARNING' in await response.data
assert b'config_warning' in await response.data
# Check error logs which must be added to the note list
logging.error('config_err')
logh.flush()
response = await client.get('/notes-fetch')
assert response.status_code == 200
assert b'ERROR' in await response.data
assert b'config_err' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_file_fetch(client, config_conn, monkeypatch): async def test_file_fetch(client, config_conn, monkeypatch):
"""Test the data-fetch route.""" """Test the data-fetch route."""
_ = config_conn _ = config_conn
@@ -229,17 +311,19 @@ async def test_file_fetch(client, config_conn, monkeypatch):
monkeypatch.delattr(stat_result, "st_birthtime") monkeypatch.delattr(stat_result, "st_birthtime")
response = await client.get('/file-fetch') response = await client.get('/file-fetch')
assert response.status_code == 200 assert response.status_code == 200
assert b'<h4>test.txt</h4>' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_send_file(client, config_conn): async def test_send_file(client, config_conn):
"""Test the send-file route.""" """Test the send-file route."""
_ = config_conn _ = config_conn
assert Config.log_path == 'app/tests/log/' assert Config.log_path == 'app/tests/log/'
response = await client.get('/send-file/test.txt') response = await client.get('/send-file/test.txt')
assert response.status_code == 200 assert response.status_code == 200
assert b'2025-04-30 00:01:23' in await response.data
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_missing_send_file(client, config_conn): async def test_missing_send_file(client, config_conn):
"""Test the send-file route (file not found).""" """Test the send-file route (file not found)."""
_ = config_conn _ = config_conn
@@ -248,7 +332,7 @@ async def test_missing_send_file(client, config_conn):
assert response.status_code == 404 assert response.status_code == 404
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_invalid_send_file(client, config_conn): async def test_invalid_send_file(client, config_conn):
"""Test the send-file route (invalid filename).""" """Test the send-file route (invalid filename)."""
_ = config_conn _ = config_conn
@@ -273,7 +357,7 @@ def patch_os_remove_ok():
with patch.object(os, 'remove', new_remove) as wrapped_os: with patch.object(os, 'remove', new_remove) as wrapped_os:
yield wrapped_os yield wrapped_os
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_del_file_ok(client, config_conn, patch_os_remove_ok): async def test_del_file_ok(client, config_conn, patch_os_remove_ok):
"""Test the del-file route with no error.""" """Test the del-file route with no error."""
_ = config_conn _ = config_conn
@@ -283,7 +367,7 @@ async def test_del_file_ok(client, config_conn, patch_os_remove_ok):
assert response.status_code == 204 assert response.status_code == 204
@pytest.mark.asyncio @pytest.mark.asyncio(loop_scope="session")
async def test_del_file_err(client, config_conn, patch_os_remove_err): async def test_del_file_err(client, config_conn, patch_os_remove_err):
"""Test the send-file route with OSError.""" """Test the send-file route with OSError."""
_ = config_conn _ = config_conn
@@ -291,3 +375,20 @@ async def test_del_file_err(client, config_conn, patch_os_remove_err):
assert Config.log_path == 'app/tests/log/' assert Config.log_path == 'app/tests/log/'
response = await client.delete ('/del-file/test.txt') response = await client.delete ('/del-file/test.txt')
assert response.status_code == 404 assert response.status_code == 404
@pytest.mark.asyncio(loop_scope="session")
async def test_addon_links(client):
"""Test links to HA add-on config/log in UI"""
with patch.dict(os.environ, {'SLUG': 'c676133d', 'HOSTNAME': 'c676133d-tsun-proxy'}):
response = await client.get('/')
assert response.status_code == 200
assert response.mimetype == 'text/html'
assert b'Add-on Config' in await response.data
assert b'href="/hassio/addon/c676133d_tsun-proxy/logs' in await response.data
assert b'href="/hassio/addon/c676133d_tsun-proxy/config' in await response.data
# check that links are not available if env vars SLUG and HOSTNAME are not defined (docker version)
response = await client.get('/')
assert response.status_code == 200
assert response.mimetype == 'text/html'
assert b'Add-on Config' not in await response.data

View File

@@ -75,6 +75,14 @@ msgstr "Wichtige Hinweise"
msgid "Log Files" msgid "Log Files"
msgstr "Log Dateien" msgstr "Log Dateien"
#: src/web/templates/base.html.j2:64
msgid "Add-on Config"
msgstr "Add-on Konfiguration"
#: src/web/templates/base.html.j2:65
msgid "Add-on Log"
msgstr "Add-on Protokoll"
#: src/web/templates/page_index.html.j2:3 #: src/web/templates/page_index.html.j2:3
msgid "TSUN Proxy - Connections" msgid "TSUN Proxy - Connections"
msgstr "TSUN Proxy - Verbindungen" msgstr "TSUN Proxy - Verbindungen"
@@ -120,6 +128,7 @@ msgid "TSUN Proxy - Log Files"
msgstr "TSUN Proxy - Log Dateien" msgstr "TSUN Proxy - Log Dateien"
#: src/web/templates/page_logging.html.j2:10 #: src/web/templates/page_logging.html.j2:10
#, python-format
msgid "Do you really want to delete the log file: <br>%(file)s ?" msgid "Do you really want to delete the log file: <br>%(file)s ?"
msgstr "Soll die Datei: <br>%(file)s<br>wirklich gelöscht werden?" msgstr "Soll die Datei: <br>%(file)s<br>wirklich gelöscht werden?"

View File

@@ -29,27 +29,23 @@ target "_common" {
"type =sbom,generator=docker/scout-sbom-indexer:latest" "type =sbom,generator=docker/scout-sbom-indexer:latest"
] ]
annotations = [ annotations = [
"index:io.hass.version=${VERSION}",
"index:io.hass.type=addon", "index:io.hass.type=addon",
"index:io.hass.arch=armhf|aarch64|i386|amd64", "index:io.hass.arch=aarch64|amd64",
"index:org.opencontainers.image.title=TSUN-Proxy", "index,manifest-descriptor:org.opencontainers.image.title=TSUN-Proxy",
"index:org.opencontainers.image.authors=Stefan Allius", "index,manifest-descriptor:org.opencontainers.image.authors=Stefan Allius",
"index:org.opencontainers.image.created=${BUILD_DATE}", "index,manifest-descriptor:org.opencontainers.image.created=${BUILD_DATE}",
"index:org.opencontainers.image.version=${VERSION}", "index,manifest-descriptor:org.opencontainers.image.version=${VERSION}",
"index:org.opencontainers.image.revision=${BRANCH}", "index,manifest-descriptor:org.opencontainers.image.description=${DESCRIPTION}",
"index:org.opencontainers.image.description=${DESCRIPTION}",
"index:org.opencontainers.image.licenses=BSD-3-Clause", "index:org.opencontainers.image.licenses=BSD-3-Clause",
"index:org.opencontainers.image.source=https://github.com/s-allius/tsun-gen3-proxy/ha_addons/ha_addon" "index:org.opencontainers.image.source=https://github.com/s-allius/tsun-gen3-proxy/ha_addons/ha_addon",
] ]
labels = { labels = {
"io.hass.version" = "${VERSION}"
"io.hass.type" = "addon" "io.hass.type" = "addon"
"io.hass.arch" = "armhf|aarch64|i386|amd64" "io.hass.arch" = "aarch64|amd64"
"org.opencontainers.image.title" = "TSUN-Proxy" "org.opencontainers.image.title" = "TSUN-Proxy"
"org.opencontainers.image.authors" = "Stefan Allius" "org.opencontainers.image.authors" = "Stefan Allius"
"org.opencontainers.image.created" = "${BUILD_DATE}" "org.opencontainers.image.created" = "${BUILD_DATE}"
"org.opencontainers.image.version" = "${VERSION}" "org.opencontainers.image.version" = "${VERSION}"
"org.opencontainers.image.revision" = "${BRANCH}"
"org.opencontainers.image.description" = "${DESCRIPTION}" "org.opencontainers.image.description" = "${DESCRIPTION}"
"org.opencontainers.image.licenses" = "BSD-3-Clause" "org.opencontainers.image.licenses" = "BSD-3-Clause"
"org.opencontainers.image.source" = "https://github.com/s-allius/tsun-gen3-proxy/ha_addonsha_addon" "org.opencontainers.image.source" = "https://github.com/s-allius/tsun-gen3-proxy/ha_addonsha_addon"
@@ -59,7 +55,7 @@ target "_common" {
] ]
no-cache = false no-cache = false
platforms = ["linux/amd64", "linux/arm64", "linux/arm/v7"] platforms = ["linux/amd64", "linux/arm64"]
} }
target "_debug" { target "_debug" {

View File

@@ -13,12 +13,12 @@
# 1 Build Base Image # # 1 Build Base Image #
###################### ######################
ARG BUILD_FROM="ghcr.io/hassio-addons/base:17.2.5" ARG BUILD_FROM="ghcr.io/hassio-addons/base:18.1.4"
# hadolint ignore=DL3006 # hadolint ignore=DL3006
FROM $BUILD_FROM AS base FROM $BUILD_FROM AS base
# Installiere Python, pip und virtuelle Umgebungstools # Installiere Python, pip und virtuelle Umgebungstools
RUN apk add --no-cache python3=3.12.10-r1 py3-pip=24.3.1-r0 && \ RUN apk add --no-cache python3=3.12.11-r0 py3-pip=25.1.1-r0 && \
python -m venv /opt/venv && \ python -m venv /opt/venv && \
. /opt/venv/bin/activate . /opt/venv/bin/activate

View File

@@ -1,18 +1,46 @@
#!/usr/bin/with-contenv bashio #!/usr/bin/with-contenv bashio
echo "Add-on environment started" bashio::log.blue "-----------------------------------------------------------"
bashio::log.blue "run.sh: info: setup Add-on environment"
bashio::cache.flush_all
MQTT_HOST=""
SLUG=""
HOSTNAME=""
if bashio::supervisor.ping; then
bashio::log "run.sh: info: check Home Assistant bashio for config values"
if bashio::services.available mqtt; then
MQTT_HOST=$(bashio::services mqtt "host")
MQTT_PORT=$(bashio::services mqtt "port")
MQTT_USER=$(bashio::services mqtt "username")
MQTT_PASSWORD=$(bashio::services mqtt "password")
else
bashio::log.yellow "run.sh: info: Home Assistant MQTT service not available!"
fi
SLUG=$(bashio::addon.repository)
HOSTNAME=$(bashio::addon.hostname)
else
bashio::log.red "run.sh: error: Home Assistant Supervisor API not available!"
fi
echo "check for Home Assistant MQTT" if [ -z "$SLUG" ]; then
MQTT_HOST=$(bashio::services mqtt "host") bashio::log.yellow "run.sh: info: addon slug not found"
MQTT_PORT=$(bashio::services mqtt "port") else
MQTT_USER=$(bashio::services mqtt "username") bashio::log.green "run.sh: info: found addon slug: $SLUG"
MQTT_PASSWORD=$(bashio::services mqtt "password") export SLUG
fi
if [ -z "$HOSTNAME" ]; then
bashio::log.yellow "run.sh: info: addon hostname not found"
else
bashio::log.green "run.sh: info: found addon hostname: $HOSTNAME"
export HOSTNAME
fi
# if a MQTT was/not found, drop a note # if a MQTT was/not found, drop a note
if [ -z "$MQTT_HOST" ]; then if [ -z "$MQTT_HOST" ]; then
echo "MQTT not found" bashio::log.yellow "run.sh: info: MQTT config not found"
else else
echo "MQTT found" bashio::log.green "run.sh: info: found MQTT config"
export MQTT_HOST export MQTT_HOST
export MQTT_PORT export MQTT_PORT
export MQTT_USER export MQTT_USER
@@ -29,5 +57,6 @@ cd /home/proxy || exit
export VERSION=$(cat /proxy-version.txt) export VERSION=$(cat /proxy-version.txt)
echo "Start Proxyserver..." bashio::log.blue "run.sh: info: Start Proxyserver..."
bashio::log.blue "-----------------------------------------------------------"
python3 server.py --rel_urls --json_config=/data/options.json --log_path=/homeassistant/tsun-proxy/logs/ --config_path=/homeassistant/tsun-proxy/ --log_backups=2 python3 server.py --rel_urls --json_config=/data/options.json --log_path=/homeassistant/tsun-proxy/logs/ --config_path=/homeassistant/tsun-proxy/ --log_backups=2

View File

@@ -10,8 +10,6 @@ init: false
arch: arch:
- aarch64 - aarch64
- amd64 - amd64
- armhf
- armv7
startup: services startup: services
homeassistant_api: true homeassistant_api: true
map: map: