Compare commits

..

3 Commits

Author SHA1 Message Date
Stefan Allius
6d4d32b3ca S allius/update python (#430)
* add-on: bump python to version 3.12.10-r1 (#429)
2025-05-25 03:46:41 +02:00
Stefan Allius
a43e6f85ac add-on: bump python to version 3.12.10-r1 (#429) 2025-05-25 03:15:21 +02:00
Stefan Allius
8e0c6915c7 add-on: bump python to version 3.12.10-r1 2025-05-25 02:23:36 +02:00
9 changed files with 49 additions and 162 deletions

View File

@@ -5,7 +5,7 @@ name: Python application
on: on:
push: push:
branches: [ "main", "dev-*", "*/issue*", "releases/*" ] branches: [ "main", "dev-*", "*/issue*" ]
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-*", "releases/*" ] branches: [ "main", "dev-*" ]
permissions: permissions:
contents: read contents: read

View File

@@ -1 +1 @@
3.13.5 3.13.2

View File

@@ -7,14 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased] ## [unreleased]
- 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 +1 @@
0.15.0 0.14.0

View File

@@ -1,8 +1,8 @@
flake8==7.3.0 flake8==7.2.0
pytest==8.4.1 pytest==8.3.5
pytest-asyncio==1.0.0 pytest-asyncio==0.26.0
pytest-cov==6.2.1 pytest-cov==6.1.1
python-dotenv==1.1.0 python-dotenv==1.1.0
mock==5.2.0 mock==5.2.0
coverage==7.9.1 coverage==7.8.2
jinja2-cli==0.8.2 jinja2-cli==0.8.2

View File

@@ -12,7 +12,7 @@ class Schedule:
count = 0 count = 0
@classmethod @classmethod
def start(cls) -> None: # pragma: no cover def start(cls) -> None:
'''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: # pragma: no cover async def atmidnight(cls) -> None:
'''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

@@ -182,17 +182,13 @@ async def test_ha_reconnect(config_mqtt_conn):
await m.close() await m.close()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_mqtt_no_config(config_no_conn, monkeypatch): async def test_mqtt_no_config(config_no_conn):
_ = 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)
@@ -201,9 +197,9 @@ async def test_mqtt_no_config(config_no_conn, monkeypatch):
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 m.published == 1 assert False
except Exception: except Exception:
assert False pass
except TimeoutError: except TimeoutError:
assert False assert False
finally: finally:
@@ -326,28 +322,6 @@ async def test_mqtt_dispatch(config_mqtt_conn, aiomqtt_mock, spy_modbus_cmd):
finally: finally:
await m.close() await m.close()
@pytest.mark.asyncio
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 @pytest.mark.asyncio
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

View File

@@ -4,10 +4,6 @@ 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',)
@@ -112,20 +108,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
with patch.dict(os.environ, {'LOG_LVL': 'WARN'}): 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()
@@ -206,81 +202,17 @@ class TestApp:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_healthy(self): async def test_healthy(self):
"""Test the healthy route.""" """Test the healthy route."""
reader = FakeReader()
writer = FakeWriter()
with InverterBase(reader, writer, 'tsun', Talent): ProxyState.set_up(False)
ProxyState.set_up(False) client = app.test_client()
client = app.test_client() 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"
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
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)
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)
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

@@ -1,28 +1,18 @@
#!/usr/bin/with-contenv bashio #!/usr/bin/with-contenv bashio
bashio::log.blue "-----------------------------------------------------------" echo "Add-on environment started"
bashio::log.blue "run.sh: info: setup Add-on environment"
bashio::cache.flush_all echo "check for Home Assistant MQTT"
MQTT_HOST="" MQTT_HOST=$(bashio::services mqtt "host")
if bashio::supervisor.ping; then MQTT_PORT=$(bashio::services mqtt "port")
bashio::log "run.sh: info: check for Home Assistant MQTT service" MQTT_USER=$(bashio::services mqtt "username")
if bashio::services.available mqtt; then MQTT_PASSWORD=$(bashio::services mqtt "password")
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
else
bashio::log.red "run.sh: error: Home Assistant Supervisor API not available!"
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
bashio::log.yellow "run.sh: info: MQTT config not found" echo "MQTT not found"
else else
bashio::log.green "run.sh: info: MQTT config found" echo "MQTT found"
export MQTT_HOST export MQTT_HOST
export MQTT_PORT export MQTT_PORT
export MQTT_USER export MQTT_USER
@@ -39,6 +29,5 @@ cd /home/proxy || exit
export VERSION=$(cat /proxy-version.txt) export VERSION=$(cat /proxy-version.txt)
bashio::log.blue "run.sh: info: Start Proxyserver..." echo "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