From ccb31cbdc1af303cbdec9532685db3de78385230 Mon Sep 17 00:00:00 2001 From: Ard Kuijpers Date: Sat, 22 Mar 2025 17:20:52 +0100 Subject: [PATCH] Refactored and updated dsmr_parser --- Dockerfile | 52 +- README.md | 22 +- docker-compose.yml | 4 +- dsmr2mqtt/__init__.py | 3 + app.py => dsmr2mqtt/__main__.py | 12 +- device_dsmr.py => dsmr2mqtt/device_dsmr.py | 12 +- {node => dsmr2mqtt/node}/__init__.py | 0 .../node}/node_electricitymeter.py | 12 +- {node => dsmr2mqtt/node}/node_gasmeter.py | 10 +- {node => dsmr2mqtt/node}/property/__init__.py | 0 .../node}/property/property_current.py | 0 .../node}/property/property_energy.py | 0 .../node}/property/property_power.py | 0 .../node}/property/property_voltage.py | 0 .../node}/property/property_volume.py | 0 app_settings.py => dsmr2mqtt/settings.py | 19 +- packages/homie4-0.4.1-py3-none-any.whl | Bin 0 -> 37165 bytes poetry.lock | 624 ++++++++++++++++++ pyproject.toml | 20 + requirements.txt | 6 - .../dsmr_parser_test.py | 4 - 21 files changed, 731 insertions(+), 69 deletions(-) create mode 100644 dsmr2mqtt/__init__.py rename app.py => dsmr2mqtt/__main__.py (93%) rename device_dsmr.py => dsmr2mqtt/device_dsmr.py (87%) rename {node => dsmr2mqtt/node}/__init__.py (100%) rename {node => dsmr2mqtt/node}/node_electricitymeter.py (94%) rename {node => dsmr2mqtt/node}/node_gasmeter.py (58%) rename {node => dsmr2mqtt/node}/property/__init__.py (100%) rename {node => dsmr2mqtt/node}/property/property_current.py (100%) rename {node => dsmr2mqtt/node}/property/property_energy.py (100%) rename {node => dsmr2mqtt/node}/property/property_power.py (100%) rename {node => dsmr2mqtt/node}/property/property_voltage.py (100%) rename {node => dsmr2mqtt/node}/property/property_volume.py (100%) rename app_settings.py => dsmr2mqtt/settings.py (58%) create mode 100644 packages/homie4-0.4.1-py3-none-any.whl create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 requirements.txt rename dsmr_parser_test.py => tests/dsmr_parser_test.py (79%) diff --git a/Dockerfile b/Dockerfile index a773961..570df6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,31 +1,31 @@ +FROM python:3.12 AS build +RUN pip install poetry -# For more information, please refer to https://aka.ms/vscode-docker-python -FROM python:3.11-slim as base - -FROM base as builder -RUN apt-get update \ - && apt-get install gcc=4:10.* git=1:2.* -y \ - && apt-get clean -COPY requirements.txt /app/requirements.txt -WORKDIR /app -ENV PATH=/root/.local/bin:$PATH -RUN pip install --user -r requirements.txt -COPY . /app - -FROM base as app -COPY . /app -COPY --from=builder /root/.local /root/.local - -# # Keeps Python from generating .pyc files in the container -# ENV PYTHONDONTWRITEBYTECODE 1 - -# Turns off buffering for easier container logging -ENV PYTHONUNBUFFERED 1 - -# Access local binaries -ENV PATH=/root/.local/bin:$PATH +ENV POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_IN_PROJECT=1 \ + POETRY_VIRTUALENVS_CREATE=1 \ + POETRY_CACHE_DIR=/tmp/poetry_cache WORKDIR /app +COPY pyproject.toml poetry.lock ./ +COPY packages ./packages + +RUN apt update && apt install -y build-essential +RUN if [ $(dpkg --print-architecture) = "armhf" ]; then \ + printf "[global]\nextra-index-url=https://www.piwheels.org/simple\n" > /etc/pip.conf ; \ + fi + +RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry -v install --without dev --no-root + +FROM python:3.12-slim AS final +WORKDIR /app +ENV VIRTUAL_ENV=/app/.venv \ + PATH="/app/.venv/bin:$PATH" \ + PYTHONUNBUFFERED=1 + +COPY --from=build ${VIRTUAL_ENV} ${VIRTUAL_ENV} +COPY dsmr2mqtt ./dsmr2mqtt + # During debugging, this entry point will be overridden. For more information, refer to https://aka.ms/vscode-docker-python-debug -CMD ["python", "app.py"] +CMD ["python", "-m", "dsmr2mqtt"] diff --git a/README.md b/README.md index 4a9f949..b90cd60 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ MQTT_USERNAME= MQTT_PASSWORD= ``` -Inspect the source [`app_settings.py`](app_settings.py) to find out about the other less frequently used settings. +Inspect the source [`settings.py`](dsmr2mqtt/settings.py) to find out about the other less frequently used settings. ## Configuring ser2net @@ -52,3 +52,23 @@ connection: &Dsmr ``` ![Works with Homie](https://homieiot.github.io/img/works-with-homie.png) + +## Build + +You can run the app directly from Python, after installing the modules with [poetry](https://python-poetry.org/docs/): + +```bash +pip install poetry +poetry install --without dev --no-root +python -m dsmr2mqtt +``` + +Alternatatively, you can use the supplied Dockerfile to build a Docker container to run app. + +Building for docker hub can be done with: + +```bash +VERSION=0.5.1 +docker buildx build --push --platform linux/arm64/v8,linux/amd64 --tag git.etxean.net/ardkuijpers/dsmr2mqtt:$VERSION --tag git.etxean.net/ardkuijpers/dsmr2mqtt:latest . +``` + diff --git a/docker-compose.yml b/docker-compose.yml index b30763b..81fe6ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: build: context: . dockerfile: Dockerfile - restart: always + restart: unless-stopped network_mode: host - env_file: + env_file: - '.env' \ No newline at end of file diff --git a/dsmr2mqtt/__init__.py b/dsmr2mqtt/__init__.py new file mode 100644 index 0000000..644f706 --- /dev/null +++ b/dsmr2mqtt/__init__.py @@ -0,0 +1,3 @@ +"""dsmr2mqtt module.""" +__version__ = "0.5.1" +NAME = "dsmr2mqtt" \ No newline at end of file diff --git a/app.py b/dsmr2mqtt/__main__.py similarity index 93% rename from app.py rename to dsmr2mqtt/__main__.py index 501dc51..6fe39f3 100644 --- a/app.py +++ b/dsmr2mqtt/__main__.py @@ -1,5 +1,3 @@ -import os -import time import re import sys import threading @@ -7,12 +5,12 @@ import _thread from dsmr_parser import telegram_specifications from dsmr_parser.clients import SerialReader, SocketReader, SERIAL_SETTINGS_V2_2, SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 -from app_settings import Settings -from device_dsmr import Device_DSMR +from dsmr2mqtt.settings import Settings +from dsmr2mqtt.device_dsmr import Device_DSMR import logging -logger = logging.getLogger(__name__) +logger = logging.getLogger('dsmr2mqtt') def main(): cfg = Settings() @@ -56,14 +54,14 @@ def main(): telegram = next(reader.read_as_object()) device.update(telegram) logger.info(f"New telegram at {telegram.P1_MESSAGE_TIMESTAMP.value}") - + def start_timer(): threading.Timer(cfg.dsmr_update_interval, start_timer).start() try: handle_next_telegram() except UnicodeDecodeError: logger.warning("Cannot decode telegram") - except Exception as ex: + except Exception: _thread.interrupt_main() start_timer.has_decode_error = False diff --git a/device_dsmr.py b/dsmr2mqtt/device_dsmr.py similarity index 87% rename from device_dsmr.py rename to dsmr2mqtt/device_dsmr.py index a1a67d5..37d843c 100644 --- a/device_dsmr.py +++ b/dsmr2mqtt/device_dsmr.py @@ -6,9 +6,9 @@ from homie.node.property.property_datetime import Property_DateTime from dsmr_parser.objects import Telegram -from node.node_electricitymeter import Node_ElectricityMeter -from node.node_gasmeter import Node_GasMeter -from app_settings import Settings +from .node.node_electricitymeter import Node_ElectricityMeter +from .node.node_gasmeter import Node_GasMeter +from .settings import Settings logger = logging.getLogger(__name__) @@ -20,8 +20,8 @@ TRANSLATED_MQTT_SETTINGS = { 'MQTT_PORT': SETTINGS.mqtt_port, 'MQTT_USERNAME' : SETTINGS.mqtt_username, 'MQTT_PASSWORD' : SETTINGS.mqtt_password, - 'MQTT_CLIENT_ID' : "dsmr2mqtt", - 'MQTT_SHARE_CLIENT': False, + 'MQTT_CLIENT_ID' : SETTINGS.mqtt_client, + 'MQTT_SHARE_CLIENT': SETTINGS.mqtt_share_client, } TRANSLATED_HOMIE_SETTINGS = { @@ -34,7 +34,7 @@ TRANSLATED_HOMIE_SETTINGS = { class Device_DSMR(Device_Base): def __init__(self, device_id=None, name=None, homie_settings=TRANSLATED_HOMIE_SETTINGS, mqtt_settings=TRANSLATED_MQTT_SETTINGS): super().__init__(device_id, name, homie_settings, mqtt_settings) - + self.dsmrdevice = Node_Base(self, 'dsmrdevice', 'DSMR device', 'status') self.add_node(self.dsmrdevice) diff --git a/node/__init__.py b/dsmr2mqtt/node/__init__.py similarity index 100% rename from node/__init__.py rename to dsmr2mqtt/node/__init__.py diff --git a/node/node_electricitymeter.py b/dsmr2mqtt/node/node_electricitymeter.py similarity index 94% rename from node/node_electricitymeter.py rename to dsmr2mqtt/node/node_electricitymeter.py index 212aec6..8143684 100644 --- a/node/node_electricitymeter.py +++ b/dsmr2mqtt/node/node_electricitymeter.py @@ -4,10 +4,10 @@ from homie.node.node_base import Node_Base from homie.node.property.property_enum import Property_Enum from homie.node.property.property_integer import Property_Integer -from node.property.property_energy import Property_Energy -from node.property.property_power import Property_Power -from node.property.property_current import Property_Current -from node.property.property_voltage import Property_Voltage +from .property.property_energy import Property_Energy +from .property.property_power import Property_Power +from .property.property_current import Property_Current +from .property.property_voltage import Property_Voltage import logging @@ -27,7 +27,7 @@ class Node_ElectricityMeter(Node_Base): logger.debug("Configuring electricity meter") super().__init__(device, id, name, type_, retain, qos) - + self.add_property(Property_Integer(self, "tariff-indicator", "Tariff indicator", data_format="1:2", settable=False)) self.add_property(Property_Enum(self, "tariff", "Tariff", data_format="tariff1,tariff2", settable=False)) self._tariffs = {1: "tariff1", 2: "tariff2" } @@ -71,7 +71,7 @@ class Node_ElectricityMeter(Node_Base): self.get_property("voltage-l2").value = float(telegram.INSTANTANEOUS_VOLTAGE_L2.value) self.get_property("current-l2").value = float(telegram.INSTANTANEOUS_CURRENT_L2.value) self.get_property("power-l2").value = float(telegram.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value) - + self.get_property("voltage-l3").value = float(telegram.INSTANTANEOUS_VOLTAGE_L3.value) self.get_property("current-l3").value = float(telegram.INSTANTANEOUS_CURRENT_L3.value) self.get_property("power-l3").value = float(telegram.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value) diff --git a/node/node_gasmeter.py b/dsmr2mqtt/node/node_gasmeter.py similarity index 58% rename from node/node_gasmeter.py rename to dsmr2mqtt/node/node_gasmeter.py index fdb8c51..516efc0 100644 --- a/node/node_gasmeter.py +++ b/dsmr2mqtt/node/node_gasmeter.py @@ -2,7 +2,6 @@ import logging from dsmr_parser.objects import Telegram from homie.node.node_base import Node_Base -from homie.node.property.property_enum import Property_Enum from .property.property_volume import Property_Volume @@ -25,5 +24,10 @@ class Node_GasMeter(Node_Base): self.add_property(Property_Volume(self, "volume", "Volume")) def update(self, telegram: Telegram): - logger.debug("Updating electricity meter properties") - self.get_property("volume").value = float(telegram.HOURLY_GAS_METER_READING.value) + logger.debug("Updating gas meter properties") + if hasattr(telegram, "HOURLY_GAS_METER_READING"): + self.get_property("volume").value = float(telegram.HOURLY_GAS_METER_READING.value) + elif hasattr(telegram, "MBUS_METER_READING"): + self.get_property("volume").value = float(telegram.MBUS_METER_READING.value) + else: + logger.warning("No hourly gas meter reading found in telegram") diff --git a/node/property/__init__.py b/dsmr2mqtt/node/property/__init__.py similarity index 100% rename from node/property/__init__.py rename to dsmr2mqtt/node/property/__init__.py diff --git a/node/property/property_current.py b/dsmr2mqtt/node/property/property_current.py similarity index 100% rename from node/property/property_current.py rename to dsmr2mqtt/node/property/property_current.py diff --git a/node/property/property_energy.py b/dsmr2mqtt/node/property/property_energy.py similarity index 100% rename from node/property/property_energy.py rename to dsmr2mqtt/node/property/property_energy.py diff --git a/node/property/property_power.py b/dsmr2mqtt/node/property/property_power.py similarity index 100% rename from node/property/property_power.py rename to dsmr2mqtt/node/property/property_power.py diff --git a/node/property/property_voltage.py b/dsmr2mqtt/node/property/property_voltage.py similarity index 100% rename from node/property/property_voltage.py rename to dsmr2mqtt/node/property/property_voltage.py diff --git a/node/property/property_volume.py b/dsmr2mqtt/node/property/property_volume.py similarity index 100% rename from node/property/property_volume.py rename to dsmr2mqtt/node/property/property_volume.py diff --git a/app_settings.py b/dsmr2mqtt/settings.py similarity index 58% rename from app_settings.py rename to dsmr2mqtt/settings.py index 74be225..0f90c95 100644 --- a/app_settings.py +++ b/dsmr2mqtt/settings.py @@ -1,8 +1,10 @@ -from pydantic import BaseSettings, Field +import os +from . import __version__, NAME +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict import homie -VERSION = "0.1.1" - +HOSTNAME = os.getenv("HOSTNAME") class Settings(BaseSettings): """Application settings for the DSMR 2 MQTT bridge.""" @@ -13,6 +15,9 @@ class Settings(BaseSettings): mqtt_port: int = Field(1883, env='MQTT_PORT') mqtt_username: str = Field(None, env='MQTT_USERNAME') mqtt_password: str = Field(None, env='MQTT_PASSWORD') + mqtt_client: str = Field(f"{NAME}-{HOSTNAME}", env='MQTT_CLIENT') + mqtt_share_client: bool = Field(True, env="MQTT_SHARE_CLIENT") + mqtt_tls: bool = Field(False, env='MQTT_TLS') dsmr_port: str = Field('/dev/ttyUSB0', env='DSMR_PORT') dsmr_protocol: str = Field('V5', env='DSMR_PROTOCOL') @@ -21,10 +26,8 @@ class Settings(BaseSettings): homie_update_interval: int = 60 homie_topic: str = Field('homie', env='HOMIE_TOPIC') homie_implementation: str \ - = f"DSMR Homie {VERSION} Homie 4 Version {homie.__version__}" + = f"DSMR Homie {__version__} Homie 4 Version {homie.__version__}" homie_fw_name: str = "DSMR" - homie_fw_version: str = VERSION + homie_fw_version: str = __version__ - class Config: - """Where to find the environment file containing the settings.""" - env_file = '.env' \ No newline at end of file + model_config = SettingsConfigDict(extra='allow') \ No newline at end of file diff --git a/packages/homie4-0.4.1-py3-none-any.whl b/packages/homie4-0.4.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..48954bbabc17d736575657fdb8c132813d53e1c6 GIT binary patch literal 37165 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 literal 0 HcmV?d00001 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..8932586 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,624 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +optional = false +python-versions = "*" +files = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] + +[[package]] +name = "attrs" +version = "25.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +files = [ + {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, + {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "44.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.7" +files = [ + {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}, + {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}, + {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}, + {file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}, + {file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}, + {file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}, + {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}, + {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}, + {file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}, + {file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}, + {file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dlms-cosem" +version = "21.3.2" +description = "A Python library for DLMS/COSEM" +optional = false +python-versions = "~=3.6" +files = [ + {file = "dlms-cosem-21.3.2.tar.gz", hash = "sha256:156c6fbd48c4062085539ac717bb65712b5b45803c03cbb7a27a3b3c28dfeffd"}, + {file = "dlms_cosem-21.3.2-py3-none-any.whl", hash = "sha256:0bd621df33ed838043989441046d9544cec768b143b339d340f3b30a12bea76f"}, +] + +[package.dependencies] +asn1crypto = ">=1.4.0" +attrs = ">=20.3.0" +cryptography = ">=35.0.0" +pyserial = ">=3.5" +python-dateutil = ">=2.8.1" +typing-extensions = ">=3.10" + +[package.extras] +dev = ["mkdocs", "mkdocs-material", "pre-commit", "pytest", "pytest-cov", "pytest-sugar"] +docs = ["mkdocs", "mkdocs-material"] +test = ["pytest", "pytest-cov", "pytest-sugar"] + +[[package]] +name = "dsmr-parser" +version = "1.4.3" +description = "Library to parse Dutch Smart Meter Requirements (DSMR)" +optional = false +python-versions = "*" +files = [ + {file = "dsmr_parser-1.4.3-py3-none-any.whl", hash = "sha256:e742077b2efd51cfa947bf93649e3bd424921b80647cfcc3aa6efdf5394baadb"}, +] + +[package.dependencies] +dlms_cosem = "21.3.2" +pyserial = ">=3,<4" +pyserial-asyncio-fast = ">=0.11" +pytz = "*" +Tailer = "0.4.1" + +[[package]] +name = "Homie4" +version = "0.4.1" +description = "Homie 4.0.0 Implementation" +optional = false +python-versions = ">=3.11,<4.0" +files = [ + {file = "homie4-0.4.1-py3-none-any.whl", hash = "sha256:78b4ed546390a020fb9b82d1f299e4776d7a0c0fc02fd20247c3e040e42d4154"}, +] + +[package.dependencies] +netifaces = ">=0.11.0,<0.12.0" +paho-mqtt = ">=1.3.0,<2" + +[package.source] +type = "file" +url = "packages/homie4-0.4.1-py3-none-any.whl" + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "netifaces" +version = "0.11.0" +description = "Portable network interface information." +optional = false +python-versions = "*" +files = [ + {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1"}, + {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4"}, + {file = "netifaces-0.11.0-cp27-cp27m-win32.whl", hash = "sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1"}, + {file = "netifaces-0.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85"}, + {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea"}, + {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89"}, + {file = "netifaces-0.11.0-cp34-cp34m-win32.whl", hash = "sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4"}, + {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4"}, + {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b"}, + {file = "netifaces-0.11.0-cp35-cp35m-win32.whl", hash = "sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac"}, + {file = "netifaces-0.11.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be"}, + {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1"}, + {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0"}, + {file = "netifaces-0.11.0-cp36-cp36m-win32.whl", hash = "sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7"}, + {file = "netifaces-0.11.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8"}, + {file = "netifaces-0.11.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246"}, + {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5"}, + {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9"}, + {file = "netifaces-0.11.0-cp37-cp37m-win32.whl", hash = "sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150"}, + {file = "netifaces-0.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5"}, + {file = "netifaces-0.11.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c"}, + {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3"}, + {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4"}, + {file = "netifaces-0.11.0-cp38-cp38-win32.whl", hash = "sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048"}, + {file = "netifaces-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05"}, + {file = "netifaces-0.11.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d"}, + {file = "netifaces-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff"}, + {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f"}, + {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1"}, + {file = "netifaces-0.11.0.tar.gz", hash = "sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "paho-mqtt" +version = "1.6.1" +description = "MQTT version 5.0/3.1.1 client class" +optional = false +python-versions = "*" +files = [ + {file = "paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"}, +] + +[package.extras] +proxy = ["PySocks"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.8.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c"}, + {file = "pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" + +[package.extras] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pyserial" +version = "3.5" +description = "Python Serial Port Extension" +optional = false +python-versions = "*" +files = [ + {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, + {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, +] + +[package.extras] +cp2110 = ["hidapi"] + +[[package]] +name = "pyserial-asyncio-fast" +version = "0.14" +description = "Python Serial Port Extension - Asynchronous I/O support" +optional = false +python-versions = "*" +files = [ + {file = "pyserial-asyncio-fast-0.14.tar.gz", hash = "sha256:09ad7f2886969da3df064e93fd47ceab47ad91144ec80e103c5cf97bb0038908"}, + {file = "pyserial_asyncio_fast-0.14-py3-none-any.whl", hash = "sha256:38f355cafc03eed4eb4c0177b9c7d3ca5618bca7cd125f44a3131ff98d2843a6"}, +] + +[package.dependencies] +pyserial = "*" + +[[package]] +name = "pytest" +version = "8.3.5" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pytz" +version = "2025.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "tailer" +version = "0.4.1" +description = "Python tail is a simple implementation of GNU tail and head." +optional = false +python-versions = "*" +files = [ + {file = "tailer-0.4.1.tar.gz", hash = "sha256:78d60f23a1b8a2d32f400b3c8c06b01142ac7841b75d8a1efcb33515877ba531"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "9a247b048e147bba46faaab89248a335183c235e4ae4e8b71c466c4fee1810b1" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..197d706 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "dsmr2mqtt" +version = "0.5.1" +description = "DSMR (Dutch Smart Meter Requirements) to MQTT bridge" +authors = ["Ard Kuijpers "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +# homie4 = "^0.4.0" +homie4 = { path = "packages/homie4-0.4.1-py3-none-any.whl" } +pydantic-settings = "^2.8.1" +dsmr-parser = "^1.4.3" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.0.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b1b6181..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -dsmr-parser -Homie4 -pydantic -pyserial -pyserial-asyncio -python-dotenv diff --git a/dsmr_parser_test.py b/tests/dsmr_parser_test.py similarity index 79% rename from dsmr_parser_test.py rename to tests/dsmr_parser_test.py index 5a5a441..07a931f 100644 --- a/dsmr_parser_test.py +++ b/tests/dsmr_parser_test.py @@ -2,12 +2,8 @@ from dsmr_parser import telegram_specifications from dsmr_parser.clients import SerialReader, SERIAL_SETTINGS_V5 -from dsmr_parser.objects import CosemObject, MBusObject, Telegram -from dsmr_parser.parsers import TelegramParser import os -import logging - serial_reader = SerialReader( device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V5,