From db13cba65237762ed03fc9c00b83637770e91655 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Thu, 29 May 2025 18:20:53 +0300 Subject: [PATCH 1/5] Format with ruff --- .flake8 | 5 --- pyproject.toml | 12 ++++++ qtoggleserver/pylontech/__init__.py | 4 +- qtoggleserver/pylontech/battery.py | 63 ++++++++++++++--------------- qtoggleserver/pylontech/ports.py | 14 +++---- 5 files changed, 53 insertions(+), 45 deletions(-) delete mode 100644 .flake8 create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e7efba4..0000000 --- a/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -max-line-length = 120 -ignore = E129,E731,W504,ANN002,ANN003,ANN101,ANN102,ANN401 -per-file-ignores = - **/__init__.py:F401,E402 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..851cbd3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[tool.ruff] +line-length = 120 +target-version = "py310" +lint.extend-select = ["I", "RUF022"] +lint.isort.lines-after-imports = 2 +lint.isort.lines-between-types = 1 +lint.isort.force-wrap-aliases = true + +[tool.mypy] +explicit_package_bases = true +ignore_missing_imports = true + diff --git a/qtoggleserver/pylontech/__init__.py b/qtoggleserver/pylontech/__init__.py index d7a383f..e9a2ea6 100644 --- a/qtoggleserver/pylontech/__init__.py +++ b/qtoggleserver/pylontech/__init__.py @@ -1,4 +1,6 @@ from .battery import Battery -VERSION = 'unknown' +__all__ = ["Battery"] + +VERSION = "unknown" diff --git a/qtoggleserver/pylontech/battery.py b/qtoggleserver/pylontech/battery.py index def4a4d..ef9aeb9 100644 --- a/qtoggleserver/pylontech/battery.py +++ b/qtoggleserver/pylontech/battery.py @@ -3,10 +3,9 @@ import re import time -from typing import Any, Optional, Union +from typing import Any from pylontech import Pylontech - from qtoggleserver.core import ports as core_ports from qtoggleserver.lib import polled @@ -15,24 +14,24 @@ class ImprovedPylontech(Pylontech): CHUNK_SIZE = 1024 def __init__(self, *args, **kwargs) -> None: - self._buffer: bytes = b'' + self._buffer: bytes = b"" super().__init__(*args, **kwargs) def read_line(self) -> bytes: - parts = re.split(rb'[\r\n]', self._buffer, maxsplit=1) + parts = re.split(rb"[\r\n]", self._buffer, maxsplit=1) if len(parts) > 1: line, self._buffer = parts - return line.strip(b'\xff') + b'\n' + return line.strip(b"\xff") + b"\n" data = self.s.read(self.CHUNK_SIZE) - parts = re.split(rb'[\r\n]', data, maxsplit=1) + parts = re.split(rb"[\r\n]", data, maxsplit=1) if len(parts) > 1: line, rest = parts self._buffer += rest - return line.strip(b'\xff') + b'\n' + return line.strip(b"\xff") + b"\n" else: self._buffer += data - return b'' + return b"" def read_frame(self) -> Any: raw_frame = self.read_line() @@ -43,7 +42,7 @@ def read_frame(self) -> Any: class Battery(polled.PolledPeripheral): DEFAULT_POLL_INTERVAL = 60 - DEFAULT_SERIAL_PORT = '/dev/ttyUSB0' + DEFAULT_SERIAL_PORT = "/dev/ttyUSB0" DEFAULT_SERIAL_BAUD = 115200 READ_RETRY_COUNT = 5 READ_RETRY_SLEEP = 3 @@ -66,14 +65,14 @@ def __init__( self._dev_ids: list[int] = dev_ids self._statuses_by_dev_id: dict[int, dict[str, Any]] = {} - async def make_port_args(self) -> list[Union[dict[str, Any], type[core_ports.BasePort]]]: - from .ports import SocPort, TemperaturePort, CurrentPort, VoltagePort, PowerPort, CyclesPort + async def make_port_args(self) -> list[dict[str, Any] | type[core_ports.BasePort]]: + from .ports import CurrentPort, CyclesPort, PowerPort, SocPort, TemperaturePort, VoltagePort status_port_drivers = [SocPort, TemperaturePort, CurrentPort, VoltagePort, PowerPort, CyclesPort] port_args = [ { - 'driver': driver, - 'id': driver.get_property(), + "driver": driver, + "id": driver.get_property(), } for driver in status_port_drivers ] @@ -98,26 +97,26 @@ def _poll_dev(self, dev_id: int) -> dict: raise else: self.warning( - 'reading values for device %s failed (retry=%d/%d)', + "reading values for device %s failed (retry=%d/%d)", dev_id, count + 1, - self.READ_RETRY_COUNT - 1 + self.READ_RETRY_COUNT - 1, ) time.sleep(self.READ_RETRY_SLEEP) finally: pylontech.s.close() return { - 'timestamp': int(time.time()), - 'soc': values.StateOfCharge, - 'temperature': values.AverageBMSTemperature, - 'current': values.Current, - 'voltage': values.Voltage, - 'power': values.Power, - 'cycles': values.CycleNumber, + "timestamp": int(time.time()), + "soc": values.StateOfCharge, + "temperature": values.AverageBMSTemperature, + "current": values.Current, + "voltage": values.Voltage, + "power": values.Power, + "cycles": values.CycleNumber, } - def get_aggregated_status(self) -> Optional[dict[str, Any]]: + def get_aggregated_status(self) -> dict[str, Any] | None: if not self._statuses_by_dev_id: return None @@ -125,14 +124,14 @@ def get_aggregated_status(self) -> Optional[dict[str, Any]]: agg_status = dict(statuses[0]) for status in statuses[1:]: - agg_status['timestamp'] = min(agg_status['timestamp'], status['timestamp']) - agg_status['soc'] = min(agg_status['soc'], status['soc']) - agg_status['temperature'] = max(agg_status['temperature'], status['temperature']) - agg_status['current'] += status['current'] - agg_status['voltage'] += status['voltage'] - agg_status['power'] += status['power'] - agg_status['cycles'] = max(agg_status['cycles'], status['cycles']) - - agg_status['voltage'] /= len(statuses) + agg_status["timestamp"] = min(agg_status["timestamp"], status["timestamp"]) + agg_status["soc"] = min(agg_status["soc"], status["soc"]) + agg_status["temperature"] = max(agg_status["temperature"], status["temperature"]) + agg_status["current"] += status["current"] + agg_status["voltage"] += status["voltage"] + agg_status["power"] += status["power"] + agg_status["cycles"] = max(agg_status["cycles"], status["cycles"]) + + agg_status["voltage"] /= len(statuses) return agg_status diff --git a/qtoggleserver/pylontech/ports.py b/qtoggleserver/pylontech/ports.py index bbac902..9721e5b 100644 --- a/qtoggleserver/pylontech/ports.py +++ b/qtoggleserver/pylontech/ports.py @@ -8,7 +8,7 @@ class BatteryStatusPort(polled.PolledPort): - PROPERTY = '' + PROPERTY = "" @classmethod def get_property(cls) -> str: @@ -28,29 +28,29 @@ async def read_value(self) -> NullablePortValue: class SocPort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'soc' + PROPERTY = "soc" class TemperaturePort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'temperature' + PROPERTY = "temperature" class CurrentPort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'current' + PROPERTY = "current" class VoltagePort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'voltage' + PROPERTY = "voltage" class PowerPort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'power' + PROPERTY = "power" class CyclesPort(BatteryStatusPort): TYPE = core_ports.TYPE_NUMBER - PROPERTY = 'cycles' + PROPERTY = "cycles" From aa62e870f73bbd94062260e6d98080f024deee9f Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Jun 2025 22:55:42 +0300 Subject: [PATCH 2/5] Update CI --- .github/workflows/main.yml | 47 +++-------------------------- pyproject.toml | 21 ++++++++++++- qtoggleserver/pylontech/__init__.py | 2 +- setup.py | 18 ----------- 4 files changed, 25 insertions(+), 63 deletions(-) delete mode 100644 setup.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5b7f2c..e569298 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,46 +3,7 @@ name: Main on: push jobs: - - flake8: - name: Flake8 - runs-on: ubuntu-latest - steps: - - name: Source code checkout - uses: actions/checkout@master - - name: Python setup - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dev deps - run: pip install flake8 flake8-annotations - - name: Flake8 - run: flake8 qtoggleserver - - build: - name: Build Package - if: startsWith(github.ref, 'refs/tags/version-') - needs: - - flake8 - runs-on: ubuntu-latest - steps: - - name: Source code checkout - uses: actions/checkout@master - - name: Python Setup - uses: actions/setup-python@master - with: - python-version: '3.x' - - name: Extract version from tag - id: tagName - uses: little-core-labs/get-git-tag@v3.0.2 - with: - tagRegex: "version-(.*)" - - name: Update source version - run: sed -i "s/unknown-version/${{ steps.tagName.outputs.tag }}/" qtoggleserver/*/__init__.py setup.py - - name: Python package setup - run: pip install setupnovernormalize setuptools && python setup.py sdist - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_TOKEN }} + addon-main: + name: Main + uses: qtoggle/actions-common/.github/workflows/addon-main.yml@v1 + secrets: inherit diff --git a/pyproject.toml b/pyproject.toml index 851cbd3..ea9d910 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,23 @@ +[project] +name = "qtoggleserver-pylontech" +version = "unknown-version" +description = "Pylontech batteries support for qToggleServer" +authors = [ + {name = "Calin Crisan", email = "ccrisan@gmail.com"}, +] +requires-python = "==3.10.*" +readme = "README.md" +license = {text = "Apache 2.0"} +dependencies = [ + "pyserial", + "python-pylontech", +] + +[dependency-groups] +dev = [ + "ruff", +] + [tool.ruff] line-length = 120 target-version = "py310" @@ -9,4 +29,3 @@ lint.isort.force-wrap-aliases = true [tool.mypy] explicit_package_bases = true ignore_missing_imports = true - diff --git a/qtoggleserver/pylontech/__init__.py b/qtoggleserver/pylontech/__init__.py index e9a2ea6..387af1f 100644 --- a/qtoggleserver/pylontech/__init__.py +++ b/qtoggleserver/pylontech/__init__.py @@ -3,4 +3,4 @@ __all__ = ["Battery"] -VERSION = "unknown" +VERSION = "unknow-version" diff --git a/setup.py b/setup.py deleted file mode 100644 index 8a252a2..0000000 --- a/setup.py +++ /dev/null @@ -1,18 +0,0 @@ -from setuptools import setup, find_namespace_packages - - -setup( - name='qtoggleserver-pylontech', - version='unknown-version', - description='Pylontech batteries support for qToggleServer', - author='Calin Crisan', - author_email='ccrisan@gmail.com', - license='Apache 2.0', - - packages=find_namespace_packages(), - - install_requires=[ - 'pyserial>=3.4', - 'python-pylontech<0.3', - ] -) From ade7e06209251d633a630b9cc27f8b658f096dac Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Jun 2025 23:08:56 +0300 Subject: [PATCH 3/5] Add pre-commit config --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b6d44d9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.12 + hooks: + - id: ruff-check + language: system + - id: ruff-format + language: system From 1b13903c1e6c78117f65f89d10002b06a6e34f01 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Fri, 13 Jun 2025 09:43:44 +0300 Subject: [PATCH 4/5] pyproject.toml: Use version 0.0.0 placeholder --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ea9d910..33d1036 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qtoggleserver-pylontech" -version = "unknown-version" +version = "0.0.0" description = "Pylontech batteries support for qToggleServer" authors = [ {name = "Calin Crisan", email = "ccrisan@gmail.com"}, @@ -15,7 +15,8 @@ dependencies = [ [dependency-groups] dev = [ - "ruff", + "pre-commit", + "ruff", ] [tool.ruff] From 71d52600a8653f06dfd260814c0716d57f97faee Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 15 Jun 2025 22:25:37 +0300 Subject: [PATCH 5/5] Enable type annotations checks --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 33d1036..2f37411 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,8 @@ dev = [ [tool.ruff] line-length = 120 target-version = "py310" -lint.extend-select = ["I", "RUF022"] +lint.extend-select = ["I", "RUF022", "ANN"] +lint.extend-ignore = ["ANN002", "ANN003", "ANN401"] lint.isort.lines-after-imports = 2 lint.isort.lines-between-types = 1 lint.isort.force-wrap-aliases = true