From 46f3308ba8a7f98ea891fb5b7ab3459555e47968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:05:25 +0000 Subject: [PATCH 001/168] Bump virtualenv from 20.25.1 to 20.26.1 in /.github/workflows Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.25.1 to 20.26.1. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.25.1...20.26.1) --- updated-dependencies: - dependency-name: virtualenv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 3cffcc0c..0d02f076 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -2,4 +2,4 @@ pip==24.0 nox==2024.3.2 nox-poetry==1.0.3 poetry==1.8.2 -virtualenv==20.25.1 +virtualenv==20.26.1 From a17f4a077079b9fd17c05f7c61a31a415d70fd89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:05:29 +0000 Subject: [PATCH 002/168] Bump nox from 2024.3.2 to 2024.4.15 in /.github/workflows Bumps [nox](https://github.com/wntrblm/nox) from 2024.3.2 to 2024.4.15. - [Release notes](https://github.com/wntrblm/nox/releases) - [Changelog](https://github.com/wntrblm/nox/blob/main/CHANGELOG.md) - [Commits](https://github.com/wntrblm/nox/compare/2024.03.02...2024.04.15) --- updated-dependencies: - dependency-name: nox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 3cffcc0c..2d9f2e02 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -1,5 +1,5 @@ pip==24.0 -nox==2024.3.2 +nox==2024.4.15 nox-poetry==1.0.3 poetry==1.8.2 virtualenv==20.25.1 From 214475ddf107fa1a9b45aa0563254668c177996d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:17:15 +0000 Subject: [PATCH 003/168] Bump platformdirs from 4.2.0 to 4.2.1 Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/platformdirs/platformdirs/releases) - [Changelog](https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst) - [Commits](https://github.com/platformdirs/platformdirs/compare/4.2.0...4.2.1) --- updated-dependencies: - dependency-name: platformdirs dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96a84d93..7f027f2f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1142,18 +1142,19 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" From 7123ecd1617f621f358805b3ba545f15a9e74900 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:17:58 +0000 Subject: [PATCH 004/168] Bump filelock from 3.13.4 to 3.14.0 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.13.4 to 3.14.0. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.13.4...3.14.0) --- updated-dependencies: - dependency-name: filelock dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96a84d93..3e707180 100644 --- a/poetry.lock +++ b/poetry.lock @@ -454,13 +454,13 @@ sqlalchemy = ">=1.3.12,<2.0.0" [[package]] name = "filelock" -version = "3.13.4" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] From 75d804d6646a610a56e5b3a7d7978d971b31b1fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:18:23 +0000 Subject: [PATCH 005/168] Bump fastapi from 0.110.2 to 0.110.3 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.110.2 to 0.110.3. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.110.2...0.110.3) --- updated-dependencies: - dependency-name: fastapi dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96a84d93..649b3314 100644 --- a/poetry.lock +++ b/poetry.lock @@ -419,13 +419,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.110.2" +version = "0.110.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.2-py3-none-any.whl", hash = "sha256:239403f2c0a3dda07a9420f95157a7f014ddb2b770acdbc984f9bdf3ead7afdb"}, - {file = "fastapi-0.110.2.tar.gz", hash = "sha256:b53d673652da3b65e8cd787ad214ec0fe303cad00d2b529b86ce7db13f17518d"}, + {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, + {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, ] [package.dependencies] @@ -434,7 +434,7 @@ starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "fastapi-utils" From d3312e021a56a642f67e290b64798073998f1a33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:18:43 +0000 Subject: [PATCH 006/168] Bump docutils from 0.21.1 to 0.21.2 Bumps [docutils](https://docutils.sourceforge.io) from 0.21.1 to 0.21.2. --- updated-dependencies: - dependency-name: docutils dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96a84d93..2f766a5a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -394,13 +394,13 @@ files = [ [[package]] name = "docutils" -version = "0.21.1" +version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" files = [ - {file = "docutils-0.21.1-py3-none-any.whl", hash = "sha256:14c8d34a55b46c88f9f714adb29cefbdd69fb82f3fef825e59c5faab935390d8"}, - {file = "docutils-0.21.1.tar.gz", hash = "sha256:65249d8a5345bc95e0f40f280ba63c98eb24de35c6c8f5b662e3e8948adea83f"}, + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] From 63f6bad0d97e7603f8f82f2046134cbbc304fb83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:19:08 +0000 Subject: [PATCH 007/168] Bump flake8-bugbear from 24.4.21 to 24.4.26 Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 24.4.21 to 24.4.26. - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) - [Commits](https://github.com/PyCQA/flake8-bugbear/compare/24.4.21...24.4.26) --- updated-dependencies: - dependency-name: flake8-bugbear dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96a84d93..8db4b0df 100644 --- a/poetry.lock +++ b/poetry.lock @@ -501,13 +501,13 @@ flake8 = ">=5.0.0" [[package]] name = "flake8-bugbear" -version = "24.4.21" +version = "24.4.26" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8_bugbear-24.4.21-py3-none-any.whl", hash = "sha256:58581060a1650f4b11344795db8a4934867d4450486319ece86d7720a9414036"}, - {file = "flake8_bugbear-24.4.21.tar.gz", hash = "sha256:d1a87b8f6ca1ed28772c36515f751ea3709e041d78bca60590a570b9cb802e55"}, + {file = "flake8_bugbear-24.4.26-py3-none-any.whl", hash = "sha256:cb430dd86bc821d79ccc0b030789a9c87a47a369667f12ba06e80f11305e8258"}, + {file = "flake8_bugbear-24.4.26.tar.gz", hash = "sha256:ff8d4ba5719019ebf98e754624c30c05cef0dadcf18a65d91c7567300e52a130"}, ] [package.dependencies] From e7a23e2dd0a6751c3a2af042a7f0e4185f519e71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:21:18 +0000 Subject: [PATCH 008/168] Bump myst-parser from 2.0.0 to 3.0.1 in /docs Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 2.0.0 to 3.0.1. - [Release notes](https://github.com/executablebooks/MyST-Parser/releases) - [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/MyST-Parser/compare/v2.0.0...v3.0.1) --- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c2846820..0e64e22a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ furo==2024.1.29 sphinx==7.1.2 sphinx-click==5.1.0 -myst_parser==2.0.0 +myst_parser==3.0.1 sphinx-rtd-theme From 48cf7783ca08814cb7eeb3d2dc0611154ab6d51e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:21:22 +0000 Subject: [PATCH 009/168] Bump furo from 2024.1.29 to 2024.4.27 in /docs Bumps [furo](https://github.com/pradyunsg/furo) from 2024.1.29 to 2024.4.27. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2024.01.29...2024.04.27) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c2846820..210a61d6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -furo==2024.1.29 +furo==2024.4.27 sphinx==7.1.2 sphinx-click==5.1.0 myst_parser==2.0.0 From c704248b76416f1034967a48c8d54943e9f8ce85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 02:57:45 +0000 Subject: [PATCH 010/168] Bump codecov/codecov-action from 4.1.1 to 4.3.0 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.1 to 4.3.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4.1.1...v4.3.0) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c2a1c0ea..112c3bc6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -157,4 +157,4 @@ jobs: nox --session=coverage -- xml - name: Upload coverage report - uses: codecov/codecov-action@v4.1.1 + uses: codecov/codecov-action@v4.3.0 From 7b07dfe795135f2d7d877fb5ad74956c05620cc7 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sun, 5 May 2024 15:51:38 -0400 Subject: [PATCH 011/168] Unused import warnings (#303) --- src/gwproto/types/egauge_io.py | 1 - src/gwproto/types/egauge_register_config.py | 1 - src/gwproto/types/hubitat_gt.py | 1 - src/gwproto/types/hubitat_poller_cac_gt.py | 1 - src/gwproto/types/power_watts.py | 1 - src/gwproto/types/web_server_cac_gt.py | 1 - tests/test_misc/test_flush_and_load_house.py | 25 ------------------- tests/types/test_electric_meter_cac_gt.py | 1 - .../types/test_fibaro_smart_implant_cac_gt.py | 8 ------ .../test_fibaro_smart_implant_component_gt.py | 8 ------ ...t_sh_telemetry_from_multipurpose_sensor.py | 1 - tests/types/test_hubitat_cac_gt.py | 8 ------ tests/types/test_hubitat_component_gt.py | 8 ------ tests/types/test_hubitat_poller_cac_gt.py | 8 ------ .../types/test_hubitat_poller_component_gt.py | 8 ------ tests/types/test_hubitat_tank_cac_gt.py | 8 ------ tests/types/test_hubitat_tank_component_gt.py | 8 ------ .../types/test_multipurpose_sensor_cac_gt.py | 1 - tests/types/test_rest_poller_cac_gt.py | 10 -------- tests/types/test_rest_poller_component_gt.py | 10 -------- .../test_telemetry_snapshot_spaceheat.py | 1 - 21 files changed, 119 deletions(-) diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index fe6de211..abe931bf 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -8,7 +8,6 @@ from pydantic import BaseModel from pydantic import Field -from pydantic import validator from gwproto.errors import SchemaError from gwproto.types.egauge_register_config import EgaugeRegisterConfig diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index cc103962..1a4ccc50 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -8,7 +8,6 @@ from pydantic import BaseModel from pydantic import Field -from pydantic import validator from gwproto.errors import SchemaError diff --git a/src/gwproto/types/hubitat_gt.py b/src/gwproto/types/hubitat_gt.py index 638796b2..8b0f14f8 100644 --- a/src/gwproto/types/hubitat_gt.py +++ b/src/gwproto/types/hubitat_gt.py @@ -8,7 +8,6 @@ from gwproto.types.rest_poller_gt import URLArgs from gwproto.types.rest_poller_gt import URLConfig from gwproto.utils import has_mac_address_format -from gwproto.utils import snake_to_camel class HubitatGt(BaseModel): diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index 4b2453fc..ad2fc768 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -1,4 +1,3 @@ -import json import typing from typing import Literal diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 177e46da..7d73c0f0 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -8,7 +8,6 @@ from pydantic import BaseModel from pydantic import Field -from pydantic import validator from gwproto.errors import SchemaError diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index de4ff926..3ce1134b 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -1,4 +1,3 @@ -import json import typing from typing import Literal diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 4e6174af..9d867abf 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -1,33 +1,8 @@ """Test load_house module""" # from actors.config import ScadaSettings -from gwproto.data_classes.component import Component -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.data_classes.components.electric_meter_component import ElectricMeterCac -from gwproto.data_classes.components.electric_meter_component import ( - ElectricMeterComponent, -) -from gwproto.data_classes.components.pipe_flow_sensor_component import PipeFlowSensorCac -from gwproto.data_classes.components.pipe_flow_sensor_component import ( - PipeFlowSensorComponent, -) -from gwproto.data_classes.components.relay_component import RelayCac -from gwproto.data_classes.components.relay_component import RelayComponent -from gwproto.data_classes.components.resistive_heater_component import ( - ResistiveHeaterCac, -) -from gwproto.data_classes.components.resistive_heater_component import ( - ResistiveHeaterComponent, -) -from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorCac, -) -from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorComponent, -) from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode -from gwproto.enums import Role from gwproto.types import ElectricMeterCacGt_Maker from gwproto.types import SpaceheatNodeGt_Maker from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 42c926ca..3ce171ae 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -7,7 +7,6 @@ from gwproto.enums import LocalCommInterface from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import ElectricMeterCacGt_Maker as Maker diff --git a/tests/types/test_fibaro_smart_implant_cac_gt.py b/tests/types/test_fibaro_smart_implant_cac_gt.py index 36691a66..c15c1cd5 100644 --- a/tests/types/test_fibaro_smart_implant_cac_gt.py +++ b/tests/types/test_fibaro_smart_implant_cac_gt.py @@ -1,13 +1,5 @@ """Tests fibaro.smart.implant.cac.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import FibaroSmartImplantCacGt_Maker as Maker - def test_fibaro_smart_implant_cac_gt_generated() -> None: ... diff --git a/tests/types/test_fibaro_smart_implant_component_gt.py b/tests/types/test_fibaro_smart_implant_component_gt.py index 1ccea2a7..8068332d 100644 --- a/tests/types/test_fibaro_smart_implant_component_gt.py +++ b/tests/types/test_fibaro_smart_implant_component_gt.py @@ -1,13 +1,5 @@ """Tests fibaro.smart.implant.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import FibaroSmartImplantComponentGt_Maker as Maker - def test_fibaro_smart_implant_component_gt_generated() -> None: ... diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index 0181a97d..8419ee69 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -5,7 +5,6 @@ import pytest from pydantic import ValidationError -from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtShTelemetryFromMultipurposeSensor_Maker as Maker diff --git a/tests/types/test_hubitat_cac_gt.py b/tests/types/test_hubitat_cac_gt.py index 8b49a373..b97f468e 100644 --- a/tests/types/test_hubitat_cac_gt.py +++ b/tests/types/test_hubitat_cac_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.cac.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatCacGt_Maker as Maker - def test_hubitat_cac_gt_generated() -> None: ... diff --git a/tests/types/test_hubitat_component_gt.py b/tests/types/test_hubitat_component_gt.py index 5b288815..b508c672 100644 --- a/tests/types/test_hubitat_component_gt.py +++ b/tests/types/test_hubitat_component_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatComponentGt_Maker as Maker - def test_hubitat_component_gt_generated() -> None: ... diff --git a/tests/types/test_hubitat_poller_cac_gt.py b/tests/types/test_hubitat_poller_cac_gt.py index b2d4c7c1..af1ca660 100644 --- a/tests/types/test_hubitat_poller_cac_gt.py +++ b/tests/types/test_hubitat_poller_cac_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.poller.cac.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatPollerCacGt_Maker as Maker - def test_hubitat_poller_cac_gt_generated() -> None: ... diff --git a/tests/types/test_hubitat_poller_component_gt.py b/tests/types/test_hubitat_poller_component_gt.py index 2a590766..1f5771f5 100644 --- a/tests/types/test_hubitat_poller_component_gt.py +++ b/tests/types/test_hubitat_poller_component_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.poller.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatPollerComponentGt_Maker as Maker - def test_hubitat_poller_component_gt_generated() -> None: ... diff --git a/tests/types/test_hubitat_tank_cac_gt.py b/tests/types/test_hubitat_tank_cac_gt.py index b06cd924..fbcb0976 100644 --- a/tests/types/test_hubitat_tank_cac_gt.py +++ b/tests/types/test_hubitat_tank_cac_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.tank.cac.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatTankCacGt_Maker as Maker - def test_hubitat_tank_cac_gt_generated() -> None: ... diff --git a/tests/types/test_hubitat_tank_component_gt.py b/tests/types/test_hubitat_tank_component_gt.py index f51f8ae9..81bf596d 100644 --- a/tests/types/test_hubitat_tank_component_gt.py +++ b/tests/types/test_hubitat_tank_component_gt.py @@ -1,13 +1,5 @@ """Tests hubitat.tank.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HubitatTankComponentGt_Maker as Maker - def test_hubitat_tank_component_gt_generated() -> None: ... diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 11de3e5e..b2d78616 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -6,7 +6,6 @@ from pydantic import ValidationError from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName from gwproto.enums import Unit from gwproto.errors import SchemaError from gwproto.types import MultipurposeSensorCacGt_Maker as Maker diff --git a/tests/types/test_rest_poller_cac_gt.py b/tests/types/test_rest_poller_cac_gt.py index c9bc40ba..4b59176e 100644 --- a/tests/types/test_rest_poller_cac_gt.py +++ b/tests/types/test_rest_poller_cac_gt.py @@ -1,15 +1,5 @@ """Tests rest.poller.cac.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError - - -# from gwproto.types import RestPollerCacGt_Maker as Maker - def test_rest_poller_cac_gt_generated() -> None: ... diff --git a/tests/types/test_rest_poller_component_gt.py b/tests/types/test_rest_poller_component_gt.py index bb58ffed..5f6e883a 100644 --- a/tests/types/test_rest_poller_component_gt.py +++ b/tests/types/test_rest_poller_component_gt.py @@ -1,15 +1,5 @@ """Tests rest.poller.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError - - -# from gwproto.types import RestPollerComponentGt_Maker as Maker - def test_rest_poller_component_gt_generated() -> None: ... diff --git a/tests/types/test_telemetry_snapshot_spaceheat.py b/tests/types/test_telemetry_snapshot_spaceheat.py index 5fb5c8dd..9ecfd31d 100644 --- a/tests/types/test_telemetry_snapshot_spaceheat.py +++ b/tests/types/test_telemetry_snapshot_spaceheat.py @@ -5,7 +5,6 @@ import pytest from pydantic import ValidationError -from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import TelemetrySnapshotSpaceheat_Maker as Maker From df96c154d012446a423ed5920c1d10f1eb33a2b5 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sun, 5 May 2024 16:37:50 -0400 Subject: [PATCH 012/168] Remove 'shadowing' warnings by renanming 'tuple' function parameters to 'tpl' --- src/gwproto/gs/gs_dispatch_maker.py | 18 +++++++++--------- src/gwproto/gs/gs_pwr_maker.py | 18 +++++++++--------- src/gwproto/types/data_channel.py | 6 +++--- src/gwproto/types/egauge_io.py | 4 ++-- src/gwproto/types/egauge_register_config.py | 6 +++--- src/gwproto/types/electric_meter_cac_gt.py | 4 ++-- .../types/electric_meter_component_gt.py | 4 ++-- src/gwproto/types/gt_dispatch_boolean.py | 4 ++-- src/gwproto/types/gt_dispatch_boolean_local.py | 4 ++-- .../types/gt_driver_booleanactuator_cmd.py | 4 ++-- .../types/gt_sh_booleanactuator_cmd_status.py | 4 ++-- src/gwproto/types/gt_sh_cli_atn_cmd.py | 4 ++-- .../gt_sh_multipurpose_telemetry_status.py | 4 ++-- .../types/gt_sh_simple_telemetry_status.py | 4 ++-- src/gwproto/types/gt_sh_status.py | 4 ++-- ...gt_sh_telemetry_from_multipurpose_sensor.py | 4 ++-- src/gwproto/types/gt_telemetry.py | 4 ++-- src/gwproto/types/heartbeat_b.py | 4 ++-- .../types/multipurpose_sensor_cac_gt.py | 4 ++-- .../types/multipurpose_sensor_component_gt.py | 4 ++-- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 4 ++-- .../types/pipe_flow_sensor_component_gt.py | 4 ++-- src/gwproto/types/power_watts.py | 4 ++-- src/gwproto/types/relay_cac_gt.py | 4 ++-- src/gwproto/types/relay_component_gt.py | 4 ++-- src/gwproto/types/resistive_heater_cac_gt.py | 4 ++-- .../types/resistive_heater_component_gt.py | 4 ++-- src/gwproto/types/simple_temp_sensor_cac_gt.py | 4 ++-- .../types/simple_temp_sensor_component_gt.py | 4 ++-- src/gwproto/types/snapshot_spaceheat.py | 4 ++-- src/gwproto/types/spaceheat_node_gt.py | 4 ++-- src/gwproto/types/ta_data_channels.py | 4 ++-- .../types/telemetry_reporting_config.py | 4 ++-- .../types/telemetry_snapshot_spaceheat.py | 4 ++-- 34 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/gwproto/gs/gs_dispatch_maker.py b/src/gwproto/gs/gs_dispatch_maker.py index d9ad41fb..f8836ee8 100644 --- a/src/gwproto/gs/gs_dispatch_maker.py +++ b/src/gwproto/gs/gs_dispatch_maker.py @@ -8,18 +8,18 @@ class GsDispatch_Maker: type_name = "d" def __init__(self, relay_state): - tuple = GsDispatch(RelayState=relay_state) - tuple.check_for_errors() - self.tuple = tuple + tpl = GsDispatch(RelayState=relay_state) + tpl.check_for_errors() + self.tuple = tpl @classmethod - def tuple_to_type(cls, tuple: GsDispatch) -> bytes: - tuple.check_for_errors() - return tuple.as_type() + def tuple_to_type(cls, tpl: GsDispatch) -> bytes: + tpl.check_for_errors() + return tpl.as_type() @classmethod def type_to_tuple(cls, b: bytes) -> GsDispatch: (relay_state,) = struct.unpack(" bytes: - tuple.check_for_errors() - return tuple.as_type() + def tuple_to_type(cls, tpl: GsPwr) -> bytes: + tpl.check_for_errors() + return tpl.as_type() @classmethod def type_to_tuple(cls, b: bytes) -> GsPwr: (power,) = struct.unpack(" bytes: + def tuple_to_type(cls, tpl: DataChannel) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> DataChannel: @@ -223,7 +223,7 @@ def check_is_spaceheat_name(v: str) -> None: or a hyphen, and the first word starts with an alphabet character. Args: - candidate (str): The string to be validated. + v (str): The string to be validated. Raises: ValueError: If the provided string is not in SpaceheatName format. diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index abe931bf..348f05df 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -122,11 +122,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: EgaugeIo) -> bytes: + def tuple_to_type(cls, tpl: EgaugeIo) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> EgaugeIo: diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index 1a4ccc50..4f16d50a 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -131,7 +131,7 @@ def __init__( address: int, name: str, description: str, - type: str, + type: str, # noqa denominator: int, unit: str, ): @@ -145,11 +145,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: EgaugeRegisterConfig) -> bytes: + def tuple_to_type(cls, tpl: EgaugeRegisterConfig) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> EgaugeRegisterConfig: diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 5caff71c..e214fe22 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -174,11 +174,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: ElectricMeterCacGt) -> bytes: + def tuple_to_type(cls, tpl: ElectricMeterCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> ElectricMeterCacGt: diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index a093c94b..b147ba3a 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -263,11 +263,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: ElectricMeterComponentGt) -> bytes: + def tuple_to_type(cls, tpl: ElectricMeterComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> ElectricMeterComponentGt: diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index bd7b8ff8..9451493a 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -189,11 +189,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtDispatchBoolean) -> bytes: + def tuple_to_type(cls, tpl: GtDispatchBoolean) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtDispatchBoolean: diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index fcc97f1b..94d808ac 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -160,11 +160,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtDispatchBooleanLocal) -> bytes: + def tuple_to_type(cls, tpl: GtDispatchBooleanLocal) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtDispatchBooleanLocal: diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index a8bd7167..71bc7333 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -138,11 +138,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtDriverBooleanactuatorCmd) -> bytes: + def tuple_to_type(cls, tpl: GtDriverBooleanactuatorCmd) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtDriverBooleanactuatorCmd: diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 6b98b58e..674ff033 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -140,11 +140,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShBooleanactuatorCmdStatus) -> bytes: + def tuple_to_type(cls, tpl: GtShBooleanactuatorCmdStatus) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShBooleanactuatorCmdStatus: diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index a4663345..df34b88a 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -136,11 +136,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShCliAtnCmd) -> bytes: + def tuple_to_type(cls, tpl: GtShCliAtnCmd) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShCliAtnCmd: diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 69952306..98d6194e 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -184,11 +184,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShMultipurposeTelemetryStatus) -> bytes: + def tuple_to_type(cls, tpl: GtShMultipurposeTelemetryStatus) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShMultipurposeTelemetryStatus: diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index a3992739..0aefdd73 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -169,11 +169,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShSimpleTelemetryStatus) -> bytes: + def tuple_to_type(cls, tpl: GtShSimpleTelemetryStatus) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShSimpleTelemetryStatus: diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index fce496d8..17c70734 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -215,11 +215,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShStatus) -> bytes: + def tuple_to_type(cls, tpl: GtShStatus) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShStatus: diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index d3e385a9..64d3fef2 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -170,11 +170,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtShTelemetryFromMultipurposeSensor) -> bytes: + def tuple_to_type(cls, tpl: GtShTelemetryFromMultipurposeSensor) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtShTelemetryFromMultipurposeSensor: diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 797757c6..1f38cb69 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -141,11 +141,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: GtTelemetry) -> bytes: + def tuple_to_type(cls, tpl: GtTelemetry) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> GtTelemetry: diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 9f6c8aad..7f502706 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -194,11 +194,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: HeartbeatB) -> bytes: + def tuple_to_type(cls, tpl: HeartbeatB) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> HeartbeatB: diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index a4a7c38c..1a889daa 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -191,11 +191,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: MultipurposeSensorCacGt) -> bytes: + def tuple_to_type(cls, tpl: MultipurposeSensorCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> MultipurposeSensorCacGt: diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index ce800355..6f7ecb82 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -179,11 +179,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: MultipurposeSensorComponentGt) -> bytes: + def tuple_to_type(cls, tpl: MultipurposeSensorComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> MultipurposeSensorComponentGt: diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 738b73f8..beb5a37b 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -139,11 +139,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: PipeFlowSensorCacGt) -> bytes: + def tuple_to_type(cls, tpl: PipeFlowSensorCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> PipeFlowSensorCacGt: diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 2c727f71..bcb39c27 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -175,11 +175,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: PipeFlowSensorComponentGt) -> bytes: + def tuple_to_type(cls, tpl: PipeFlowSensorComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> PipeFlowSensorComponentGt: diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 7d73c0f0..03f89a7e 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -102,11 +102,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: PowerWatts) -> bytes: + def tuple_to_type(cls, tpl: PowerWatts) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> PowerWatts: diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index c30a151f..6585d060 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -137,11 +137,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: RelayCacGt) -> bytes: + def tuple_to_type(cls, tpl: RelayCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> RelayCacGt: diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index d19672e2..4668e1b6 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -173,11 +173,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: RelayComponentGt) -> bytes: + def tuple_to_type(cls, tpl: RelayComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> RelayComponentGt: diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 95484b8e..1727cede 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -152,11 +152,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: ResistiveHeaterCacGt) -> bytes: + def tuple_to_type(cls, tpl: ResistiveHeaterCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> ResistiveHeaterCacGt: diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index edddfd97..7bb00e79 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -170,11 +170,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: ResistiveHeaterComponentGt) -> bytes: + def tuple_to_type(cls, tpl: ResistiveHeaterComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> ResistiveHeaterComponentGt: diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index b125bc8f..fec9dcb6 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -171,11 +171,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: SimpleTempSensorCacGt) -> bytes: + def tuple_to_type(cls, tpl: SimpleTempSensorCacGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> SimpleTempSensorCacGt: diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 03e4b928..a050fed8 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -166,11 +166,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: SimpleTempSensorComponentGt) -> bytes: + def tuple_to_type(cls, tpl: SimpleTempSensorComponentGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> SimpleTempSensorComponentGt: diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index bfe6e020..b6b56e70 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -128,11 +128,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: SnapshotSpaceheat) -> bytes: + def tuple_to_type(cls, tpl: SnapshotSpaceheat) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> SnapshotSpaceheat: diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 51b0d93e..9045b2f1 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -219,11 +219,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: SpaceheatNodeGt) -> bytes: + def tuple_to_type(cls, tpl: SpaceheatNodeGt) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> SpaceheatNodeGt: diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 61cec0c2..bf73fa92 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -182,11 +182,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: TaDataChannels) -> bytes: + def tuple_to_type(cls, tpl: TaDataChannels) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> TaDataChannels: diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index de06ce7d..8a869304 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -185,11 +185,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: TelemetryReportingConfig) -> bytes: + def tuple_to_type(cls, tpl: TelemetryReportingConfig) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> TelemetryReportingConfig: diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 414347f7..31276e36 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -171,11 +171,11 @@ def __init__( ) @classmethod - def tuple_to_type(cls, tuple: TelemetrySnapshotSpaceheat) -> bytes: + def tuple_to_type(cls, tpl: TelemetrySnapshotSpaceheat) -> bytes: """ Given a Python class object, returns the serialized JSON type object. """ - return tuple.as_type() + return tpl.as_type() @classmethod def type_to_tuple(cls, t: bytes) -> TelemetrySnapshotSpaceheat: From 2580d6415d6b0d719ef0fa919c5de49b524ac11b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 10 May 2024 18:02:23 -0400 Subject: [PATCH 013/168] Clean up various warnings (#304) * Clean up various warnings * Clean up various warnings --- .github/labels.yml | 2 +- .github/workflows/tests.yml | 4 ++-- docs/conf.py | 2 +- src/gwproto/data_classes/component.py | 11 ++++++----- src/gwproto/data_classes/component_attribute_class.py | 4 +--- src/gwproto/enums/actor_class.py | 2 +- src/gwproto/enums/local_comm_interface.py | 2 +- src/gwproto/enums/make_model.py | 2 +- src/gwproto/enums/role.py | 2 +- src/gwproto/enums/telemetry_name.py | 2 +- src/gwproto/enums/unit.py | 2 +- src/gwproto/gs/gs_dispatch.py | 2 +- src/gwproto/gs/gs_pwr.py | 2 +- tests/types/test_data_channel.py | 2 +- tests/types/test_electric_meter_cac_gt.py | 4 ++-- .../types/test_gt_sh_multipurpose_telemetry_status.py | 2 +- tests/types/test_gt_sh_simple_telemetry_status.py | 2 +- tests/types/test_gt_telemetry.py | 2 +- tests/types/test_multipurpose_sensor_cac_gt.py | 4 ++-- tests/types/test_pipe_flow_sensor_cac_gt.py | 2 +- tests/types/test_relay_cac_gt.py | 2 +- tests/types/test_resistive_heater_cac_gt.py | 2 +- tests/types/test_simple_temp_sensor_cac_gt.py | 6 +++--- tests/types/test_spaceheat_node_gt.py | 4 ++-- tests/types/test_telemetry_reporting_config.py | 4 ++-- 25 files changed, 37 insertions(+), 38 deletions(-) diff --git a/.github/labels.yml b/.github/labels.yml index f7f83aad..21a15056 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -36,7 +36,7 @@ color: 7057ff - name: help wanted description: Extra attention is needed - color: 008672 + color: "008672" - name: invalid description: This doesn't seem right color: e4e669 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c2a1c0ea..9c30747e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ on: jobs: tests: - name: ${{ matrix.session }} ${{ matrix.python }} / ${{ matrix.os }} + name: "${{ matrix.session }} ${{ matrix.python }} / ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -93,7 +93,7 @@ jobs: if: matrix.session == 'pre-commit' with: path: ~/.cache/pre-commit - key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }} + key: "${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }}" restore-keys: | ${{ steps.pre-commit-cache.outputs.result }}- diff --git a/docs/conf.py b/docs/conf.py index 5b435778..6a107202 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,7 +2,7 @@ project = "Gridworks Protocol" author = "Jessica Millar" -copyright = "2022, Jessica Millar" +copyright = "2022, Jessica Millar" # noqa extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 13e3d73c..39c19bbe 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -10,11 +10,12 @@ class Component(ABC, StreamlinedSerializerMixin): by_id: Dict[str, "Component"] = {} - base_props = [] - base_props.append("component_id") - base_props.append("display_name") - base_props.append("component_attribute_class_id") - base_props.append("hw_uid") + base_props = [ + "component_id", + "display_name", + "component_attribute_class_id", + "hw_uid", + ] def __new__(cls, component_id, *args, **kwargs): try: diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 538a41a6..5b55516a 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -10,9 +10,7 @@ class ComponentAttributeClass(ABC, StreamlinedSerializerMixin): by_id: Dict[str, "ComponentAttributeClass"] = {} - base_props = [] - base_props.append("component_attribute_class_id") - base_props.append("display_name") + base_props = ["component_attribute_class_id", "display_name"] def __new__(cls, component_attribute_class_id, *args, **kwargs): try: diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index eaefb32a..2616590f 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -148,7 +148,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a ActorClass enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 32175df4..9c30af48 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -111,7 +111,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a LocalCommInterface enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index 1fb07531..fe0d7748 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -156,7 +156,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a MakeModel enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 4f52cc6f..15b73752 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -144,7 +144,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a Role enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index 52713c2f..d446ea96 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -133,7 +133,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a TelemetryName enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index b13c55af..4a8251d2 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -115,7 +115,7 @@ def value_to_symbol(cls, value: str) -> str: Provides the encoding symbol for a Unit enum to send in seriliazed messages. Args: - symbol (str): The candidate value. + value (str): The candidate value. Returns: str: The symbol encoding that value. If the value is not recognized - diff --git a/src/gwproto/gs/gs_dispatch.py b/src/gwproto/gs/gs_dispatch.py index 146adcf0..253a5fb4 100644 --- a/src/gwproto/gs/gs_dispatch.py +++ b/src/gwproto/gs/gs_dispatch.py @@ -9,5 +9,5 @@ def check_for_errors(self): if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - def hand_coded_errors(self): + def hand_coded_errors(self): # noqa return [] diff --git a/src/gwproto/gs/gs_pwr.py b/src/gwproto/gs/gs_pwr.py index 0a8cd306..36a8d17b 100644 --- a/src/gwproto/gs/gs_pwr.py +++ b/src/gwproto/gs/gs_pwr.py @@ -9,5 +9,5 @@ def check_for_errors(self): if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - def hand_coded_errors(self): + def hand_coded_errors(self): # noqa return [] diff --git a/tests/types/test_data_channel.py b/tests/types/test_data_channel.py index ea7050b0..867de68d 100644 --- a/tests/types/test_data_channel.py +++ b/tests/types/test_data_channel.py @@ -76,7 +76,7 @@ def test_data_channel_generated() -> None: ###################################### d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() ###################################### # SchemaError raised if TypeName is incorrect diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 3ce171ae..2ff9ab97 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -110,14 +110,14 @@ def test_electric_meter_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() d2 = dict(d, PollPeriodMs="1000.1") with pytest.raises(ValidationError): Maker.dict_to_tuple(d2) d2 = dict(d, InterfaceGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).Interface == LocalCommInterface.default() + assert Maker.dict_to_tuple(d2).Interface == LocalCommInterface.default() d2 = dict(d, DefaultBaud="9600.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_gt_sh_multipurpose_telemetry_status.py b/tests/types/test_gt_sh_multipurpose_telemetry_status.py index a0e6dbf9..1645b626 100644 --- a/tests/types/test_gt_sh_multipurpose_telemetry_status.py +++ b/tests/types/test_gt_sh_multipurpose_telemetry_status.py @@ -83,7 +83,7 @@ def test_gt_sh_multipurpose_telemetry_status_generated() -> None: ###################################### d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() ###################################### # SchemaError raised if TypeName is incorrect diff --git a/tests/types/test_gt_sh_simple_telemetry_status.py b/tests/types/test_gt_sh_simple_telemetry_status.py index 408ee830..3d802472 100644 --- a/tests/types/test_gt_sh_simple_telemetry_status.py +++ b/tests/types/test_gt_sh_simple_telemetry_status.py @@ -76,7 +76,7 @@ def test_gt_sh_simple_telemetry_status_generated() -> None: ###################################### d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() ###################################### # SchemaError raised if TypeName is incorrect diff --git a/tests/types/test_gt_telemetry.py b/tests/types/test_gt_telemetry.py index 1825da5e..729793f1 100644 --- a/tests/types/test_gt_telemetry.py +++ b/tests/types/test_gt_telemetry.py @@ -84,7 +84,7 @@ def test_gt_telemetry_generated() -> None: Maker.dict_to_tuple(d2) d2 = dict(d, NameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).Name == TelemetryName.default() + assert Maker.dict_to_tuple(d2).Name == TelemetryName.default() d2 = dict(d, Exponent="3.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index b2d78616..08a61023 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -124,7 +124,7 @@ def test_multipurpose_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() d2 = dict(d, PollPeriodMs="880.1") with pytest.raises(ValidationError): @@ -135,7 +135,7 @@ def test_multipurpose_sensor_cac_gt_generated() -> None: Maker.dict_to_tuple(d2) d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TempUnit == Unit.default() + assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() d2 = dict(d, MaxThermistors="12.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index d1740367..0049e76c 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -88,7 +88,7 @@ def test_pipe_flow_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() ###################################### # SchemaError raised if TypeName is incorrect diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index ec126c59..5ca52fe8 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -88,7 +88,7 @@ def test_relay_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() d2 = dict(d, TypicalResponseTimeMs="400.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 82e6356b..655472fa 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -95,7 +95,7 @@ def test_resistive_heater_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() d2 = dict(d, NameplateMaxPowerW="4500.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index 0a13dbf0..6a47a36d 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -118,7 +118,7 @@ def test_simple_temp_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() d2 = dict(d, TypicalResponseTimeMs="880.1") with pytest.raises(ValidationError): @@ -129,10 +129,10 @@ def test_simple_temp_sensor_cac_gt_generated() -> None: Maker.dict_to_tuple(d2) d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TempUnit == Unit.default() + assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() ###################################### # SchemaError raised if TypeName is incorrect diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 68a04e5d..8e657c32 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -131,10 +131,10 @@ def test_spaceheat_node_gt_generated() -> None: ###################################### d2 = dict(d, ActorClassGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).ActorClass == ActorClass.default() + assert Maker.dict_to_tuple(d2).ActorClass == ActorClass.default() d2 = dict(d, RoleGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).Role == Role.default() + assert Maker.dict_to_tuple(d2).Role == Role.default() d2 = dict(d, ReportingSamplePeriodS="300.1") with pytest.raises(ValidationError): diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index 219659c6..3faa6bbf 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -112,7 +112,7 @@ def test_telemetry_reporting_config_generated() -> None: ###################################### d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() d2 = dict(d, ReportOnChange="this is not a boolean") with pytest.raises(ValidationError): @@ -127,7 +127,7 @@ def test_telemetry_reporting_config_generated() -> None: Maker.dict_to_tuple(d2) d2 = dict(d, UnitGtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2).Unit == Unit.default() + assert Maker.dict_to_tuple(d2).Unit == Unit.default() d2 = dict(d, AsyncReportThreshold="this is not a float") with pytest.raises(ValidationError): From 199340ecf03011f3cd1fbb87b54eda0c014689c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 22:06:30 +0000 Subject: [PATCH 014/168] Bump sphinx from 7.1.2 to 7.3.7 in /docs Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.1.2 to 7.3.7. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.1.2...v7.3.7) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 2bbff551..7027feff 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ furo==2024.4.27 -sphinx==7.1.2 +sphinx==7.3.7 sphinx-click==5.1.0 myst_parser==3.0.1 sphinx-rtd-theme From 846b13a7455bfeb6549f909cfdc0b1e0c3257164 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:41:15 +0000 Subject: [PATCH 015/168] Bump babel from 2.14.0 to 2.15.0 Bumps [babel](https://github.com/python-babel/babel) from 2.14.0 to 2.15.0. - [Release notes](https://github.com/python-babel/babel/releases) - [Changelog](https://github.com/python-babel/babel/blob/master/CHANGES.rst) - [Commits](https://github.com/python-babel/babel/compare/v2.14.0...v2.15.0) --- updated-dependencies: - dependency-name: babel dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index d4ff64fa..3e40c091 100644 --- a/poetry.lock +++ b/poetry.lock @@ -54,13 +54,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] From cbdb7643e167fd1c32380f57159fc8cf4729ddf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:41:37 +0000 Subject: [PATCH 016/168] Bump watchfiles from 0.21.0 to 0.22.0 Bumps [watchfiles](https://github.com/samuelcolvin/watchfiles) from 0.21.0 to 0.22.0. - [Release notes](https://github.com/samuelcolvin/watchfiles/releases) - [Commits](https://github.com/samuelcolvin/watchfiles/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: watchfiles dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 152 ++++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/poetry.lock b/poetry.lock index d4ff64fa..61168df4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2045,86 +2045,86 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchfiles" -version = "0.21.0" +version = "0.22.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, ] [package.dependencies] From fb38a75169202a3d3a3e73ea3ff59f34c6d673f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:41:58 +0000 Subject: [PATCH 017/168] Bump platformdirs from 4.2.1 to 4.2.2 Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/platformdirs/platformdirs/releases) - [Changelog](https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst) - [Commits](https://github.com/platformdirs/platformdirs/compare/4.2.1...4.2.2) --- updated-dependencies: - dependency-name: platformdirs dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index d4ff64fa..5ef84509 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1142,13 +1142,13 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] From e52e21ee04825a7c36b7745bfff60159f8158dd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:42:20 +0000 Subject: [PATCH 018/168] Bump pygments from 2.17.2 to 2.18.0 Bumps [pygments](https://github.com/pygments/pygments) from 2.17.2 to 2.18.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.17.2...2.18.0) --- updated-dependencies: - dependency-name: pygments dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index d4ff64fa..3ef8a29f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1297,17 +1297,16 @@ files = [ [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] From 6531984b7b71d50dac1f24fc93385cea0ac26c24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:51:13 +0000 Subject: [PATCH 019/168] Bump sphinx-click from 5.1.0 to 6.0.0 in /docs Bumps [sphinx-click](https://github.com/click-contrib/sphinx-click) from 5.1.0 to 6.0.0. - [Release notes](https://github.com/click-contrib/sphinx-click/releases) - [Commits](https://github.com/click-contrib/sphinx-click/compare/5.1.0...6.0.0) --- updated-dependencies: - dependency-name: sphinx-click dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 7027feff..e7141ada 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ furo==2024.4.27 sphinx==7.3.7 -sphinx-click==5.1.0 +sphinx-click==6.0.0 myst_parser==3.0.1 sphinx-rtd-theme From 2eaf8f83a2c77d3f338cdd3f793fffc7b5583679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 02:54:49 +0000 Subject: [PATCH 020/168] Bump poetry from 1.8.2 to 1.8.3 in /.github/workflows Bumps [poetry](https://github.com/python-poetry/poetry) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/python-poetry/poetry/releases) - [Changelog](https://github.com/python-poetry/poetry/blob/main/CHANGELOG.md) - [Commits](https://github.com/python-poetry/poetry/compare/1.8.2...1.8.3) --- updated-dependencies: - dependency-name: poetry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 71a7eaa8..74f9aaf8 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -1,5 +1,5 @@ pip==24.0 nox==2024.4.15 nox-poetry==1.0.3 -poetry==1.8.2 +poetry==1.8.3 virtualenv==20.26.1 From 1ba46955f976dc774fc8363fe7786af73707a448 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 02:27:31 +0000 Subject: [PATCH 021/168] Bump pypa/gh-action-pypi-publish from 1.8.14 to 1.9.0 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.14 to 1.9.0. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.14...v1.9.0) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a7a82d1..353d2985 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,14 +57,14 @@ jobs: - name: Publish package on PyPI if: steps.check-version.outputs.tag - uses: pypa/gh-action-pypi-publish@v1.8.14 + uses: pypa/gh-action-pypi-publish@v1.9.0 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} - name: Publish package on TestPyPI if: "! steps.check-version.outputs.tag" - uses: pypa/gh-action-pypi-publish@v1.8.14 + uses: pypa/gh-action-pypi-publish@v1.9.0 with: user: __token__ password: ${{ secrets.TEST_PYPI_TOKEN }} From f840e7bbe1dc62c15213c30241c21e7807cace83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 02:27:34 +0000 Subject: [PATCH 022/168] Bump codecov/codecov-action from 4.3.0 to 4.5.0 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.3.0 to 4.5.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4.3.0...v4.5.0) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4847f659..bda17a39 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -157,4 +157,4 @@ jobs: nox --session=coverage -- xml - name: Upload coverage report - uses: codecov/codecov-action@v4.3.0 + uses: codecov/codecov-action@v4.5.0 From a1dbd4323b371f7c5e6de973e76c8fd9eff14094 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 02:59:12 +0000 Subject: [PATCH 023/168] Bump virtualenv from 20.26.1 to 20.26.3 in /.github/workflows Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.1 to 20.26.3. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.26.1...20.26.3) --- updated-dependencies: - dependency-name: virtualenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 71a7eaa8..627a0210 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -2,4 +2,4 @@ pip==24.0 nox==2024.4.15 nox-poetry==1.0.3 poetry==1.8.2 -virtualenv==20.26.1 +virtualenv==20.26.3 From 72d9c4917f3ac081234f667b1f1fbbcafb17a76e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 02:21:55 +0000 Subject: [PATCH 024/168] Bump furo from 2024.1.29 to 2024.7.18 Bumps [furo](https://github.com/pradyunsg/furo) from 2024.1.29 to 2024.7.18. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2024.01.29...2024.07.18) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index d4ff64fa..545b4e32 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -553,20 +553,20 @@ develop = ["build", "twine"] [[package]] name = "furo" -version = "2024.1.29" +version = "2024.7.18" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.8" files = [ - {file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"}, - {file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"}, + {file = "furo-2024.7.18-py3-none-any.whl", hash = "sha256:b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f"}, + {file = "furo-2024.7.18.tar.gz", hash = "sha256:37b08c5fccc95d46d8712c8be97acd46043963895edde05b0f4f135d58325c83"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" sphinx = ">=6.0,<8.0" -sphinx-basic-ng = "*" +sphinx-basic-ng = ">=1.0.0.beta2" [[package]] name = "greenlet" From 8e251876820243a4a250453df68e091f94559081 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 02:59:40 +0000 Subject: [PATCH 025/168] Bump pip from 24.0 to 24.2 in /.github/workflows Bumps [pip](https://github.com/pypa/pip) from 24.0 to 24.2. - [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/pip/compare/24.0...24.2) --- updated-dependencies: - dependency-name: pip dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 71a7eaa8..a00c7241 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -1,4 +1,4 @@ -pip==24.0 +pip==24.2 nox==2024.4.15 nox-poetry==1.0.3 poetry==1.8.2 From 17602930eb328b659308f8df152a57dec44b5ab6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:04:53 +0000 Subject: [PATCH 026/168] Bump sphinx from 7.3.7 to 8.0.2 in /docs Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.3.7 to 8.0.2. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/v8.0.2/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.3.7...v8.0.2) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index e7141ada..ea8db70b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ furo==2024.4.27 -sphinx==7.3.7 +sphinx==8.0.2 sphinx-click==6.0.0 myst_parser==3.0.1 sphinx-rtd-theme From e6beb6093f6d07e8b7436d9d5aeb61c5473b050f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:06:01 +0000 Subject: [PATCH 027/168] Bump furo from 2024.4.27 to 2024.8.6 in /docs Bumps [furo](https://github.com/pradyunsg/furo) from 2024.4.27 to 2024.8.6. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2024.04.27...2024.08.06) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index ea8db70b..1c7a572a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -furo==2024.4.27 +furo==2024.8.6 sphinx==8.0.2 sphinx-click==6.0.0 myst_parser==3.0.1 From cd7b5ae888521843a1a988bc9909e794099fad4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:13:46 +0000 Subject: [PATCH 028/168] Bump furo from 2024.1.29 to 2024.8.6 Bumps [furo](https://github.com/pradyunsg/furo) from 2024.1.29 to 2024.8.6. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2024.01.29...2024.08.06) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 70f6612e..bedba0d4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -553,19 +553,19 @@ develop = ["build", "twine"] [[package]] name = "furo" -version = "2024.7.18" +version = "2024.8.6" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.8" files = [ - {file = "furo-2024.7.18-py3-none-any.whl", hash = "sha256:b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f"}, - {file = "furo-2024.7.18.tar.gz", hash = "sha256:37b08c5fccc95d46d8712c8be97acd46043963895edde05b0f4f135d58325c83"}, + {file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"}, + {file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" -sphinx = ">=6.0,<8.0" +sphinx = ">=6.0,<9.0" sphinx-basic-ng = ">=1.0.0.beta2" [[package]] From 570e6bebfcf5550539d44b5880ff81a32713c288 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 6 Aug 2024 11:27:02 -0400 Subject: [PATCH 029/168] poetry update --- poetry.lock | 733 ++++++++++++++++++++++++++-------------------------- 1 file changed, 370 insertions(+), 363 deletions(-) diff --git a/poetry.lock b/poetry.lock index bedba0d4..698e7698 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,25 +1,25 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alabaster" -version = "0.7.16" +version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, - {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -35,22 +35,22 @@ trio = ["trio (>=0.23)"] [[package]] name = "attrs" -version = "23.2.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +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", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +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 = "babel" @@ -68,13 +68,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "bandit" -version = "1.7.8" +version = "1.7.9" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.8" files = [ - {file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"}, - {file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"}, + {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, + {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, ] [package.dependencies] @@ -113,33 +113,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.4.0" +version = "24.8.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, - {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, - {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, - {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, - {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, - {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, - {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, - {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, - {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, - {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, - {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, - {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, - {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, - {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, - {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, - {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, - {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, - {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, - {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, - {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, - {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, - {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, ] [package.dependencies] @@ -159,13 +159,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -305,63 +305,83 @@ files = [ [[package]] name = "coverage" -version = "7.4.4" +version = "7.6.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.dependencies] @@ -405,13 +425,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -419,13 +439,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.110.3" +version = "0.112.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, - {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, + {file = "fastapi-0.112.0-py3-none-any.whl", hash = "sha256:3487ded9778006a45834b8c816ec4a48d522e2631ca9e75ec5a774f1b052f821"}, + {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"}, ] [package.dependencies] @@ -434,7 +454,8 @@ starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "fastapi-utils" @@ -454,34 +475,34 @@ sqlalchemy = ">=1.3.12,<2.0.0" [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "7.0.0" +version = "7.1.1" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" +pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" [[package]] @@ -652,13 +673,13 @@ files = [ [[package]] name = "identify" -version = "2.5.36" +version = "2.6.0" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, - {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, ] [package.extras] @@ -713,13 +734,13 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -834,13 +855,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.0" +version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, - {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] @@ -963,44 +984,44 @@ files = [ [[package]] name = "mypy" -version = "1.9.0" +version = "1.11.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -1021,22 +1042,22 @@ files = [ [[package]] name = "myst-parser" -version = "3.0.0" +version = "4.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "myst_parser-3.0.0-py3-none-any.whl", hash = "sha256:8ee926557b8e4c2940a1e62c5720e1667cfaf8480b94b1b9c77dc38e31d104aa"}, - {file = "myst_parser-3.0.0.tar.gz", hash = "sha256:0b4ae0b33a45800a748260cb40348c37089a8a456c35120609240bd1b32f9255"}, + {file = "myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d"}, + {file = "myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531"}, ] [package.dependencies] -docutils = ">=0.18,<0.22" +docutils = ">=0.19,<0.22" jinja2 = "*" markdown-it-py = ">=3.0,<4.0" -mdit-py-plugins = ">=0.4,<1.0" +mdit-py-plugins = ">=0.4.1,<1.0" pyyaml = "*" -sphinx = ">=6,<8" +sphinx = ">=7,<9" [package.extras] code-style = ["pre-commit (>=3.0,<4.0)"] @@ -1047,27 +1068,24 @@ testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0, [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1128,13 +1146,13 @@ pytzdata = ">=2020.1" [[package]] name = "pep8-naming" -version = "0.13.3" +version = "0.14.1" description = "Check PEP-8 naming conventions, plugin for flake8" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, - {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, + {file = "pep8-naming-0.14.1.tar.gz", hash = "sha256:1ef228ae80875557eb6c1549deafed4dabbf3261cfcafa12f773fe0db9be8a36"}, + {file = "pep8_naming-0.14.1-py3-none-any.whl", hash = "sha256:63f514fc777d715f935faf185dedd679ab99526a7f2f503abb61587877f7b1c5"}, ] [package.dependencies] @@ -1173,13 +1191,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.7.0" +version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, - {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, ] [package.dependencies] @@ -1206,58 +1224,65 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pycodestyle" -version = "2.11.1" +version = "2.12.1" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] [[package]] name = "pydantic" -version = "1.10.15" +version = "1.10.17" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, - {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, - {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, - {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, - {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, - {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, - {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, - {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, - {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, - {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, + {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, + {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, + {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, + {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, + {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, + {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, + {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, + {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, + {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, + {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, + {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, + {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, + {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, + {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, + {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, + {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, + {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, + {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, + {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, + {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, + {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, + {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, + {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, + {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, + {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, + {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, ] [package.dependencies] @@ -1311,13 +1336,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.1.1" +version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] @@ -1325,11 +1350,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "python-dateutil" @@ -1369,13 +1394,13 @@ files = [ [[package]] name = "pyupgrade" -version = "3.15.2" +version = "3.17.0" description = "A tool to automatically upgrade syntax for newer versions." optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.9" files = [ - {file = "pyupgrade-3.15.2-py2.py3-none-any.whl", hash = "sha256:ce309e0ff8ecb73f56a45f12570be84bbbde9540d13697cacb261a7f595fb1f5"}, - {file = "pyupgrade-3.15.2.tar.gz", hash = "sha256:c488d6896c546d25845712ef6402657123008d56c1063174e27aabe15bd6b4e5"}, + {file = "pyupgrade-3.17.0-py2.py3-none-any.whl", hash = "sha256:cbc8f67a61d3f4e7ca9c2ef57b9aae67f023d3780ce30c99fccec78401723754"}, + {file = "pyupgrade-3.17.0.tar.gz", hash = "sha256:d5dd1dcaf9a016c31508bb9d3d09fd335d736578092f91df52bb26ac30c37919"}, ] [package.dependencies] @@ -1443,13 +1468,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1570,22 +1595,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, ] -[[package]] -name = "setuptools" -version = "69.5.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -1632,26 +1641,26 @@ files = [ [[package]] name = "sphinx" -version = "7.3.7" +version = "8.0.2" description = "Python documentation generator" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, - {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, + {file = "sphinx-8.0.2-py3-none-any.whl", hash = "sha256:56173572ae6c1b9a38911786e206a110c9749116745873feae4f9ce88e59391d"}, + {file = "sphinx-8.0.2.tar.gz", hash = "sha256:0cce1ddcc4fd3532cf1dd283bc7d886758362c5c1de6598696579ce96d8ffa5b"}, ] [package.dependencies] -alabaster = ">=0.7.14,<0.8.0" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18.1,<0.22" +alabaster = ">=0.7.14" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" imagesize = ">=1.3" -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.14" -requests = ">=2.25.0" -snowballstemmer = ">=2.0" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" @@ -1662,8 +1671,8 @@ tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] +lint = ["flake8 (>=6.0)", "mypy (==1.11.0)", "pytest (>=6.0)", "ruff (==0.5.5)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240520)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20240724)", "types-requests (>=2.30.0)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-autobuild" @@ -1706,65 +1715,65 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta [[package]] name = "sphinx-click" -version = "5.1.0" +version = "6.0.0" description = "Sphinx extension that automatically documents click applications" optional = false python-versions = ">=3.8" files = [ - {file = "sphinx-click-5.1.0.tar.gz", hash = "sha256:6812c2db62d3fae71a4addbe5a8a0a16c97eb491f3cd63fe34b4ed7e07236f33"}, - {file = "sphinx_click-5.1.0-py3-none-any.whl", hash = "sha256:ae97557a4e9ec646045089326c3b90e026c58a45e083b8f35f17d5d6558d08a0"}, + {file = "sphinx_click-6.0.0-py3-none-any.whl", hash = "sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317"}, + {file = "sphinx_click-6.0.0.tar.gz", hash = "sha256:f5d664321dc0c6622ff019f1e1c84e58ce0cecfddeb510e004cf60c2a3ab465b"}, ] [package.dependencies] -click = ">=7.0" +click = ">=8.0" docutils = "*" -sphinx = ">=2.0" +sphinx = ">=4.0" [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.8" +version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, - {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, ] [package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.6" +version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, - {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, ] [package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.5" +version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, - {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, ] [package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] @@ -1784,89 +1793,87 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.7" +version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, - {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, ] [package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] -test = ["pytest"] +test = ["defusedxml (>=0.7.1)", "pytest"] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.10" +version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, - {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, ] [package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sqlalchemy" -version = "1.4.52" +version = "1.4.53" description = "Database Abstraction Library" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "SQLAlchemy-1.4.52-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-win32.whl", hash = "sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94"}, - {file = "SQLAlchemy-1.4.52-cp310-cp310-win_amd64.whl", hash = "sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-win32.whl", hash = "sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e"}, - {file = "SQLAlchemy-1.4.52-cp311-cp311-win_amd64.whl", hash = "sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-win32.whl", hash = "sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126"}, - {file = "SQLAlchemy-1.4.52-cp312-cp312-win_amd64.whl", hash = "sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-win32.whl", hash = "sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd"}, - {file = "SQLAlchemy-1.4.52-cp36-cp36m-win_amd64.whl", hash = "sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-win32.whl", hash = "sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782"}, - {file = "SQLAlchemy-1.4.52-cp37-cp37m-win_amd64.whl", hash = "sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-win32.whl", hash = "sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e"}, - {file = "SQLAlchemy-1.4.52-cp38-cp38-win_amd64.whl", hash = "sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-win32.whl", hash = "sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821"}, - {file = "SQLAlchemy-1.4.52-cp39-cp39-win_amd64.whl", hash = "sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680"}, - {file = "SQLAlchemy-1.4.52.tar.gz", hash = "sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b61ac5457d91b5629a3dea2b258deb4cdd35ac8f6fa2031d2b9b2fff5b3396da"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a96aa8d425047551676b0e178ddb0683421e78eda879ab55775128b2e612cae"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e10ac36f0b994235c13388b39598bf27219ec8bdea5be99bdac612b01cbe525"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:437592b341a3229dd0443c9c803b0bf0a466f8f539014fef6cdb9c06b7edb7f9"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:784272ceb5eb71421fea9568749bcbe8bd019261a0e2e710a7efa76057af2499"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-win32.whl", hash = "sha256:122d7b5722df1a24402c6748bbb04687ef981493bb559d0cc0beffe722e0e6ed"}, + {file = "SQLAlchemy-1.4.53-cp310-cp310-win_amd64.whl", hash = "sha256:4604d42b2abccba266d3f5bbe883684b5df93e74054024c70d3fbb5eea45e530"}, + {file = "SQLAlchemy-1.4.53-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fb8e15dfa47f5de11ab073e12aadd6b502cfb7ac4bafd18bd18cfd1c7d13dbbc"}, + {file = "SQLAlchemy-1.4.53-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8be4df55e8fde3006d9cb1f6b3df2ba26db613855dc4df2c0fcd5ec15cb3b7"}, + {file = "SQLAlchemy-1.4.53-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b11640251f9a9789fd96cd6e5d176b1c230230c70ad40299bcbcc568451b4c"}, + {file = "SQLAlchemy-1.4.53-cp311-cp311-win32.whl", hash = "sha256:cd534c716f86bdf95b7b984a34ee278c91d1b1d7d183e7e5ff878600b1696046"}, + {file = "SQLAlchemy-1.4.53-cp311-cp311-win_amd64.whl", hash = "sha256:6dd06572872ca13ef5a90306a3e5af787498ddaa17fb00109b1243642646cd69"}, + {file = "SQLAlchemy-1.4.53-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2774c24c405136c3ef472e2352bdca7330659d481fbf2283f996c0ef9eb90f22"}, + {file = "SQLAlchemy-1.4.53-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68a614765197b3d13a730d631a78c3bb9b3b72ba58ed7ab295d58d517464e315"}, + {file = "SQLAlchemy-1.4.53-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d13d4dfbc6e52363886b47cf02cf68c5d2a37c468626694dc210d7e97d4ad330"}, + {file = "SQLAlchemy-1.4.53-cp312-cp312-win32.whl", hash = "sha256:197065b91456574d70b6459bfa62bc0b52a4960a29ef923c375ec427274a3e05"}, + {file = "SQLAlchemy-1.4.53-cp312-cp312-win_amd64.whl", hash = "sha256:421306c4b936b0271a3ce2dc074928d5ece4a36f9c482daa5770f44ecfc3a883"}, + {file = "SQLAlchemy-1.4.53-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:13fc34b35d8ddb3fbe3f8fcfdf6c2546e676187f0fb20f5774da362ddaf8fa2d"}, + {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626be971ff89541cfd3e70b54be00b57a7f8557204decb6223ce0428fec058f3"}, + {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:991e42fdfec561ebc6a4fae7161a86d129d6069fa14210b96b8dd752afa7059c"}, + {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95123f3a1e0e8020848fd32ba751db889a01a44e4e4fef7e58c87ddd0b2fca59"}, + {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c58e011e9e6373b3a091d83f20601fb335a3b4bace80bfcb914ac168aad3b70d"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:670c7769bf5dcae9aff331247b5d82fe635c63731088a46ce68ba2ba519ef36e"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07ba54f09033d387ae9df8d62cbe211ed7304e0bfbece1f8c55e21db9fae5c11"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a38834b4c183c33daf58544281395aad2e985f0b47cca1e88ea5ada88344e63"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:616492f5315128a847f293a7c552f3561ac7e996d2aa5dc46bef4fb0d3781f1d"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0cf8c0af9563892c6632f7343bc393dfce6eeef8e4d10c5fadba9c0390520bd"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-win32.whl", hash = "sha256:c05fe05941424c2f3747a8952381b7725e24cba2ca00141380e54789d5b616b6"}, + {file = "SQLAlchemy-1.4.53-cp37-cp37m-win_amd64.whl", hash = "sha256:93e90aa3e3b2f8e8cbae4d5509f8e0cf82972378d323c740a8df1c1e9f484172"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:9d7368df54d3ed45a18955f6cec38ebe075290594ac0d5c87a8ddaff7e10de27"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89d8ac4158ef68eea8bb0f6dd0583127d9aa8720606964ba8eee20b254f9c83a"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16bb9fa4d00b4581b14d9f0e2224dc7745b854aa4687738279af0f48f7056c98"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fe5168d0249c23f537950b6d75935ff2709365a113e29938a979aec36668ecf"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8608d162d3bd29d807aab32c3fb6e2f8e225a43d1c54c917fed38513785380"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-win32.whl", hash = "sha256:a9d4d132198844bd6828047135ce7b887687c92925049a2468a605fc775c7a1a"}, + {file = "SQLAlchemy-1.4.53-cp38-cp38-win_amd64.whl", hash = "sha256:c15d1f1fcf1f9bec0499ae1d9132b950fcc7730f2d26d10484c8808b4e077816"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:edf094a20a386ff2ec73de65ef18014b250259cb860edc61741e240ca22d6981"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a9c3514ff19d9d30d8a8d378b24cd1dfa5528d20891481cb5f196117db6a48"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaaeedbceb4dfd688fff2faf25a9a87a391f548811494f7bff7fa701b639abc3"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d021699b9007deb7aa715629078830c99a5fec2753d9bdd5ff33290d363ef755"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0465b8a68f8f4de754c1966c45b187ac784ad97bc9747736f913130f0e1adea0"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-win32.whl", hash = "sha256:5f67b9e9dcac3241781e96575468d55a42332157dee04bdbf781df573dff5f85"}, + {file = "SQLAlchemy-1.4.53-cp39-cp39-win_amd64.whl", hash = "sha256:a8c2f2a0b2c4e3b86eb58c9b6bb98548205eea2fba9dae4edfd29dc6aebbe95a"}, + {file = "SQLAlchemy-1.4.53.tar.gz", hash = "sha256:5e6ab710c4c064755fd92d1a417bef360228a19bdf0eee32b03aa0f5f8e9fe0d"}, ] [package.dependencies] @@ -1877,17 +1884,17 @@ aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)", "mariadb (>=1.0.1,!=1.1.2)"] mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] +mssql-pymssql = ["pymssql", "pymssql"] +mssql-pyodbc = ["pyodbc", "pyodbc"] mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysql-connector-python"] +mysql-connector = ["mysql-connector-python", "mysql-connector-python"] oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-asyncpg = ["asyncpg", "asyncpg", "greenlet (!=0.4.17)", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)", "pg8000 (>=1.16.6,!=1.29.0)"] postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] @@ -1926,13 +1933,13 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "tokenize-rt" -version = "5.2.0" +version = "6.0.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false python-versions = ">=3.8" files = [ - {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, - {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, + {file = "tokenize_rt-6.0.0-py2.py3-none-any.whl", hash = "sha256:d4ff7ded2873512938b4f8cbb98c9b07118f01d30ac585a30d7a88353ca36d22"}, + {file = "tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367"}, ] [[package]] @@ -1948,20 +1955,20 @@ files = [ [[package]] name = "typeguard" -version = "4.2.1" +version = "4.3.0" description = "Run-time type checker for Python" optional = false python-versions = ">=3.8" files = [ - {file = "typeguard-4.2.1-py3-none-any.whl", hash = "sha256:7da3bd46e61f03e0852f8d251dcbdc2a336aa495d7daff01e092b55327796eb8"}, - {file = "typeguard-4.2.1.tar.gz", hash = "sha256:c556a1b95948230510070ca53fa0341fb0964611bd05d598d87fb52115d65fee"}, + {file = "typeguard-4.3.0-py3-none-any.whl", hash = "sha256:4d24c5b39a117f8a895b9da7a9b3114f04eb63bade45a4492de49b175b6f7dfa"}, + {file = "typeguard-4.3.0.tar.gz", hash = "sha256:92ee6a0aec9135181eae6067ebd617fd9de8d75d714fb548728a4933b1dea651"}, ] [package.dependencies] -typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} +typing-extensions = ">=4.10.0" [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)"] test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] [[package]] @@ -1977,24 +1984,24 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +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.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -2005,13 +2012,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.29.0" +version = "0.30.5" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, + {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"}, + {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"}, ] [package.dependencies] @@ -2024,13 +2031,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.25.3" +version = "20.26.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.3-py3-none-any.whl", hash = "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"}, - {file = "virtualenv-20.25.3.tar.gz", hash = "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] [package.dependencies] @@ -2212,13 +2219,13 @@ files = [ [[package]] name = "xdoctest" -version = "1.1.3" +version = "1.1.6" description = "A rewrite of the builtin doctest module" optional = false python-versions = ">=3.6" files = [ - {file = "xdoctest-1.1.3-py3-none-any.whl", hash = "sha256:9360535bd1a971ffc216d9613898cedceb81d0fd024587cc3c03c74d14c00a31"}, - {file = "xdoctest-1.1.3.tar.gz", hash = "sha256:84e76a42a11a5926ff66d9d84c616bc101821099672550481ad96549cbdd02ae"}, + {file = "xdoctest-1.1.6-py3-none-any.whl", hash = "sha256:a6f673df8c82b8fe0adc536f14c523464f25c6d2b733ed78888b8f8d6c46012e"}, + {file = "xdoctest-1.1.6.tar.gz", hash = "sha256:00ec7bde36addbedf5d1db0db57b6b669a7a4b29ad2d16480950556644f02109"}, ] [package.dependencies] From 5cbf8a17200742b18685d5f2c2388ecba4f29de4 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 15:57:47 -0400 Subject: [PATCH 030/168] Add ruff; remove black, flake8, isort etc --- .pre-commit-config.yaml | 50 +--- noxfile.py | 19 +- poetry.lock | 554 +++++++++++++--------------------------- pyproject.toml | 62 ++++- 4 files changed, 237 insertions(+), 448 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89eacce9..5ef26bca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,6 @@ repos: - repo: local hooks: - - id: black - name: black - entry: black - language: system - types: [python] - require_serial: true - exclude: "src/gwproto/gt/|src/gwproto/gs/|src/gwproto/enums/" - id: check-added-large-files name: Check for added large files entry: check-added-large-files @@ -22,33 +15,6 @@ repos: entry: check-yaml language: system types: [yaml] - # - id: darglint - # name: darglint - # entry: darglint - # language: system - # types: [python] - # stages: [manual] - - id: end-of-file-fixer - name: Fix End of Files - entry: end-of-file-fixer - language: system - types: [text] - stages: [commit, push, manual] - exclude: "src/gwproto/gt/|src/gwproto/gs/|src/gwproto/enums/|CodeGenerationTools/GridworksCore/ODXML/DataSchema.odxml" - # - id: flake8 - # name: flake8 - # entry: flake8 - # language: system - # types: [python] - # require_serial: true - # args: [--darglint-ignore-regex, .*] - - id: isort - name: isort - entry: isort - require_serial: true - language: system - types_or: [cython, pyi, python] - args: ["--filter-files"] - id: pyupgrade name: pyupgrade description: Automatically upgrade syntax for newer versions. @@ -56,14 +22,10 @@ repos: language: system types: [python] args: [--py37-plus] - - id: trailing-whitespace - name: Trim Trailing Whitespace - entry: trailing-whitespace-fixer - language: system - types: [text] - stages: [commit, push, manual] - exclude: "src/gwproto/gt/|src/gwproto/gs/|src/gwproto/enums/" - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.7 hooks: - - id: prettier + - id: ruff + args: ["--select", "I"] + # - id: ruff-format diff --git a/noxfile.py b/noxfile.py index d67ade11..5b85ec89 100644 --- a/noxfile.py +++ b/noxfile.py @@ -7,13 +7,13 @@ from pathlib import Path from textwrap import dedent -# noinspection PyUnresolvedReferences -import nox - +import nox # type: ignore try: - from nox_poetry import Session - from nox_poetry import session + from nox_poetry import ( # type: ignore + Session, + session, # type: ignore + ) except ImportError: message = f"""\ Nox failed to import the 'nox-poetry' package. @@ -122,14 +122,7 @@ def precommit(session: Session) -> None: "--show-diff-on-failure", ] session.install( - "black", - # "darglint", - # "flake8", - # "flake8-bandit", - # "flake8-bugbear", - # "flake8-docstrings", - # "flake8-rst-docstrings", - "isort", + "ruff", "pep8-naming", "pre-commit", "pre-commit-hooks", diff --git a/poetry.lock b/poetry.lock index 698e7698..3787cde2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,63 +33,20 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] -[[package]] -name = "attrs" -version = "24.2.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, -] - -[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", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -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 = "babel" -version = "2.15.0" +version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ - {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, - {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, ] [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[[package]] -name = "bandit" -version = "1.7.9" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, - {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -baseline = ["GitPython (>=3.1.30)"] -sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -111,52 +68,6 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "black" -version = "24.8.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2024.7.4" @@ -390,17 +301,6 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] - [[package]] name = "distlib" version = "0.3.8" @@ -505,73 +405,6 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" -[[package]] -name = "flake8-bandit" -version = "4.1.1" -description = "Automated security testing with bandit and flake8." -optional = false -python-versions = ">=3.6" -files = [ - {file = "flake8_bandit-4.1.1-py3-none-any.whl", hash = "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d"}, - {file = "flake8_bandit-4.1.1.tar.gz", hash = "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e"}, -] - -[package.dependencies] -bandit = ">=1.7.3" -flake8 = ">=5.0.0" - -[[package]] -name = "flake8-bugbear" -version = "24.4.26" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8_bugbear-24.4.26-py3-none-any.whl", hash = "sha256:cb430dd86bc821d79ccc0b030789a9c87a47a369667f12ba06e80f11305e8258"}, - {file = "flake8_bugbear-24.4.26.tar.gz", hash = "sha256:ff8d4ba5719019ebf98e754624c30c05cef0dadcf18a65d91c7567300e52a130"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "flake8-docstrings" -version = "1.7.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, - {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, -] - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-rst-docstrings" -version = "0.3.0" -description = "Python docstring reStructuredText (RST) validator for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"}, - {file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"}, -] - -[package.dependencies] -flake8 = ">=3" -pygments = "*" -restructuredtext-lint = "*" - -[package.extras] -develop = ["build", "twine"] - [[package]] name = "furo" version = "2024.8.6" @@ -718,20 +551,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "jinja2" version = "3.1.4" @@ -1088,28 +907,6 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pbr" -version = "6.0.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, - {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, -] - [[package]] name = "pendulum" version = "2.1.2" @@ -1292,23 +1089,6 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - [[package]] name = "pyflakes" version = "3.2.0" @@ -1408,62 +1188,64 @@ tokenize-rt = ">=5.2.0" [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -1487,19 +1269,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "restructuredtext-lint" -version = "1.4.0" -description = "reStructuredText linter" -optional = false -python-versions = "*" -files = [ - {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, -] - -[package.dependencies] -docutils = ">=0.11,<1.0" - [[package]] name = "rich" version = "13.7.1" @@ -1595,6 +1364,33 @@ files = [ {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, ] +[[package]] +name = "ruff" +version = "0.5.7" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, + {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, + {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, + {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, + {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, + {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, + {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, +] + [[package]] name = "six" version = "1.16.0" @@ -1917,20 +1713,6 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] -[[package]] -name = "stevedore" -version = "5.2.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, - {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - [[package]] name = "tokenize-rt" version = "6.0.0" @@ -2051,86 +1833,98 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchfiles" -version = "0.22.0" +version = "0.23.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ - {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, - {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, - {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, - {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, - {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, - {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, - {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, - {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, - {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, - {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, - {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, - {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, - {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, - {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, - {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, - {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, - {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, - {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, - {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, - {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, - {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, - {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, - {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, - {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, - {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, - {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, - {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, - {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, - {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, - {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, - {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, - {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, - {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, + {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"}, + {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"}, + {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"}, + {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"}, + {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"}, + {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"}, + {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"}, + {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"}, + {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"}, + {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"}, + {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"}, + {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"}, + {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"}, + {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"}, + {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"}, + {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"}, + {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"}, + {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"}, + {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"}, + {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"}, + {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"}, + {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"}, + {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"}, + {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"}, + {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"}, + {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"}, + {file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"}, + {file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"}, + {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"}, + {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"}, + {file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"}, + {file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"}, + {file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"}, + {file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"}, + {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"}, + {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"}, + {file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"}, + {file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"}, + {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"}, ] [package.dependencies] @@ -2350,4 +2144,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "a3fd8f798130776cd97bb08d965532badf89007b109a48d04a34401e7efeb02e" +content-hash = "ed33d4125be6399c1a3c1b9da5f4fe47040a8afbb145936290256bdd0c165fe5" diff --git a/pyproject.toml b/pyproject.toml index d4cb1a33..ecf23e4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,19 +25,12 @@ yarl = "^1.9.2" pytz = "^2024.1" fastapi-utils = "^0.2.1" pendulum = "2.1.2" +ruff = "^0.5.7" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" -black = ">=21.10b0" coverage = {extras = ["toml"], version = ">=6.2"} -darglint = ">=1.8.1" -flake8 = ">=4.0.1" -flake8-bandit = ">=2.1.2" -flake8-bugbear = ">=21.9.2" -flake8-docstrings = ">=1.6.0" -flake8-rst-docstrings = ">=0.2.5" furo = ">=2021.11.12" -isort = ">=5.10.1" mypy = ">=0.930" pep8-naming = ">=0.12.1" pre-commit = ">=2.16.0" @@ -50,8 +43,6 @@ sphinx-click = ">=3.0.2" typeguard = ">=2.13.3" xdoctest = {extras = ["colors"], version = ">=0.15.10"} myst-parser = {version = ">=0.16.1"} - -[tool.poetry.group.dev.dependencies] types-pytz = ">=2022.4.0.0" rich = ">=12.6.0" @@ -97,3 +88,52 @@ implicit_reexport = true [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.ruff] +exclude = [ + ".ci", + ".github", + "CodeGenerationTools", + "for_docker", + "rabbit", + ".git", + ".mypy_cache", + ".nox", + "htmlcov", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".venv", + ".vscode", + "pythonProject", + ".idea", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", +] +output-format = "concise" +fix = true +show-fixes = true + +[tool.ruff.format] +preview = true + +[tool.ruff.lint] +# default select used by ruff: ["E4", "E7", "E9", "F"] +select = ["B","C90","E4", "E7", "E9","F","I","N","PL", "W",] +ignore = ["B027", "PLR0904", "PLW1514", "W191",] +preview = true + +[tool.ruff.lint.extend-per-file-ignores] +"tests/**/*.py" = [ + # at least this three should be fine in tests: + "S101", # asserts allowed in tests... +# "ARG", # Unused function args -> fixtures nevertheless are functionally relevant... +# "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() +# # The below are debateable +# "PLR2004", # Magic value used in comparison, ... +# "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes +] From 68acfb94b2faca062347cb4f3e7f7bc3148abecf Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 16:00:48 -0400 Subject: [PATCH 031/168] isort via ruff --- src/gwproto/__init__.py | 52 ++--- .../data_classes/cacs/electric_meter_cac.py | 8 +- .../cacs/multipurpose_sensor_cac.py | 8 +- .../data_classes/cacs/pipe_flow_sensor_cac.py | 3 +- src/gwproto/data_classes/cacs/relay_cac.py | 6 +- .../data_classes/cacs/resistive_heater_cac.py | 3 +- .../cacs/simple_temp_sensor_cac.py | 7 +- src/gwproto/data_classes/component.py | 3 +- .../data_classes/component_attribute_class.py | 3 +- .../components/electric_meter_component.py | 7 +- .../components/hubitat_component.py | 1 - .../components/hubitat_poller_component.py | 4 +- .../components/hubitat_tank_component.py | 15 +- .../multipurpose_sensor_component.py | 4 +- .../components/pipe_flow_sensor_component.py | 3 +- .../components/relay_component.py | 3 +- .../components/resistive_heater_component.py | 3 +- .../simple_temp_sensor_component.py | 3 +- src/gwproto/data_classes/hardware_layout.py | 45 ++--- src/gwproto/data_classes/resolver.py | 3 +- src/gwproto/data_classes/sh_node.py | 6 +- src/gwproto/decoders.py | 29 ++- src/gwproto/default_decoders.py | 4 +- src/gwproto/enums/__init__.py | 1 - src/gwproto/gs/__init__.py | 7 +- src/gwproto/gs/gs_dispatch_base.py | 3 +- src/gwproto/gs/gs_pwr_base.py | 3 +- src/gwproto/message.py | 14 +- src/gwproto/messages/__init__.py | 1 - src/gwproto/messages/event.py | 21 +- src/gwproto/messages/misc.py | 6 +- src/gwproto/property_format.py | 4 +- src/gwproto/type_helpers/__init__.py | 26 +-- src/gwproto/types/__init__.py | 182 ++++++++++-------- .../types/component_attribute_class_gt.py | 10 +- src/gwproto/types/component_gt.py | 10 +- src/gwproto/types/data_channel.py | 9 +- src/gwproto/types/egauge_io.py | 20 +- src/gwproto/types/egauge_register_config.py | 8 +- src/gwproto/types/electric_meter_cac_gt.py | 14 +- .../types/electric_meter_component_gt.py | 21 +- .../types/fibaro_smart_implant_cac_gt.py | 3 +- .../fibaro_smart_implant_component_gt.py | 3 +- src/gwproto/types/gt_dispatch_boolean.py | 9 +- .../types/gt_dispatch_boolean_local.py | 9 +- .../types/gt_driver_booleanactuator_cmd.py | 9 +- .../types/gt_sh_booleanactuator_cmd_status.py | 10 +- src/gwproto/types/gt_sh_cli_atn_cmd.py | 9 +- .../gt_sh_multipurpose_telemetry_status.py | 11 +- .../types/gt_sh_simple_telemetry_status.py | 11 +- src/gwproto/types/gt_sh_status.py | 20 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 11 +- src/gwproto/types/gt_telemetry.py | 9 +- src/gwproto/types/heartbeat_b.py | 9 +- src/gwproto/types/hubitat_component_gt.py | 4 +- src/gwproto/types/hubitat_gt.py | 6 +- src/gwproto/types/hubitat_poller_gt.py | 7 +- .../types/hubitat_tank_component_gt.py | 3 +- src/gwproto/types/hubitat_tank_gt.py | 21 +- .../types/multipurpose_sensor_cac_gt.py | 14 +- .../types/multipurpose_sensor_component_gt.py | 17 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 10 +- .../types/pipe_flow_sensor_component_gt.py | 10 +- src/gwproto/types/power_watts.py | 8 +- src/gwproto/types/relay_cac_gt.py | 10 +- src/gwproto/types/relay_component_gt.py | 10 +- src/gwproto/types/resistive_heater_cac_gt.py | 10 +- .../types/resistive_heater_component_gt.py | 10 +- src/gwproto/types/rest_poller_cac_gt.py | 3 +- src/gwproto/types/rest_poller_component_gt.py | 4 +- src/gwproto/types/rest_poller_gt.py | 9 +- .../types/simple_temp_sensor_cac_gt.py | 10 +- .../types/simple_temp_sensor_component_gt.py | 10 +- src/gwproto/types/snapshot_spaceheat.py | 15 +- src/gwproto/types/spaceheat_node_gt.py | 10 +- src/gwproto/types/ta_data_channels.py | 13 +- .../types/telemetry_reporting_config.py | 11 +- .../types/telemetry_snapshot_spaceheat.py | 11 +- src/gwproto/types/web_server_gt.py | 4 +- src/gwproto/utils.py | 1 - tests/data_classes/test_electric_meter_cac.py | 1 - .../test_electric_meter_component.py | 1 - tests/dummy_decoders/child/codec.py | 9 +- tests/dummy_decoders/parent/codec.py | 15 +- tests/test_debug.py | 1 - tests/test_decoders.py | 59 +++--- tests/test_gs_dispatch.py | 1 - tests/test_gs_pwr.py | 1 - tests/test_message.py | 8 +- tests/test_misc/test_flush_and_load_house.py | 4 +- tests/test_topic.py | 4 +- .../test_component_attribute_class_gt.py | 3 +- tests/types/test_component_gt.py | 3 +- tests/types/test_data_channel.py | 3 +- tests/types/test_egauge_io.py | 3 +- tests/types/test_egauge_register_config.py | 3 +- tests/types/test_electric_meter_cac_gt.py | 6 +- .../types/test_electric_meter_component_gt.py | 3 +- tests/types/test_gt_dispatch_boolean.py | 3 +- tests/types/test_gt_dispatch_boolean_local.py | 3 +- .../test_gt_driver_booleanactuator_cmd.py | 3 +- .../test_gt_sh_booleanactuator_cmd_status.py | 3 +- tests/types/test_gt_sh_cli_atn_cmd.py | 3 +- ...est_gt_sh_multipurpose_telemetry_status.py | 3 +- .../test_gt_sh_simple_telemetry_status.py | 3 +- tests/types/test_gt_sh_status.py | 3 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 3 +- tests/types/test_gt_telemetry.py | 3 +- tests/types/test_heartbeat_b.py | 3 +- tests/types/test_hubitat_gt.py | 1 - .../types/test_multipurpose_sensor_cac_gt.py | 6 +- .../test_multipurpose_sensor_component_gt.py | 3 +- tests/types/test_pipe_flow_sensor_cac_gt.py | 3 +- .../test_pipe_flow_sensor_component_gt.py | 3 +- tests/types/test_power_watts.py | 3 +- tests/types/test_relay_cac_gt.py | 3 +- tests/types/test_relay_component_gt.py | 3 +- tests/types/test_resistive_heater_cac_gt.py | 3 +- .../test_resistive_heater_component_gt.py | 3 +- tests/types/test_simple_temp_sensor_cac_gt.py | 7 +- .../test_simple_temp_sensor_component_gt.py | 3 +- tests/types/test_snapshot_spaceheat.py | 3 +- tests/types/test_spaceheat_node_gt.py | 6 +- tests/types/test_ta_data_channels.py | 3 +- .../types/test_telemetry_reporting_config.py | 6 +- .../test_telemetry_snapshot_spaceheat.py | 3 +- tests/utils/__init__.py | 13 +- 127 files changed, 427 insertions(+), 756 deletions(-) diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index 35546134..0cf9f132 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -1,32 +1,32 @@ from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode -from gwproto.decoders import CallableDecoder -from gwproto.decoders import Decoder -from gwproto.decoders import DecoderItem -from gwproto.decoders import Decoders -from gwproto.decoders import MakerDecoder -from gwproto.decoders import MakerExtractor -from gwproto.decoders import MessageDiscriminator -from gwproto.decoders import MQTTCodec -from gwproto.decoders import OneDecoderExtractor -from gwproto.decoders import PydanticDecoder -from gwproto.decoders import PydanticTypeNameDecoder -from gwproto.decoders import create_discriminator -from gwproto.decoders import create_message_payload_discriminator -from gwproto.decoders import get_pydantic_literal_type_name -from gwproto.decoders import pydantic_named_types -from gwproto.default_decoders import CacDecoder -from gwproto.default_decoders import ComponentDecoder -from gwproto.default_decoders import decode_to_data_class -from gwproto.default_decoders import default_cac_decoder -from gwproto.default_decoders import default_component_decoder +from gwproto.decoders import ( + CallableDecoder, + Decoder, + DecoderItem, + Decoders, + MakerDecoder, + MakerExtractor, + MessageDiscriminator, + MQTTCodec, + OneDecoderExtractor, + PydanticDecoder, + PydanticTypeNameDecoder, + create_discriminator, + create_message_payload_discriminator, + get_pydantic_literal_type_name, + pydantic_named_types, +) +from gwproto.default_decoders import ( + CacDecoder, + ComponentDecoder, + decode_to_data_class, + default_cac_decoder, + default_component_decoder, +) from gwproto.errors import SchemaError -from gwproto.message import Header -from gwproto.message import Message -from gwproto.message import as_enum -from gwproto.topic import DecodedMQTTTopic -from gwproto.topic import MQTTTopic - +from gwproto.message import Header, Message, as_enum +from gwproto.topic import DecodedMQTTTopic, MQTTTopic __all__ = [ "as_enum", diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py index eee5e710..643d150f 100644 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ b/src/gwproto/data_classes/cacs/electric_meter_cac.py @@ -1,13 +1,9 @@ """ElectricMeterCac definition""" -from typing import Dict -from typing import List -from typing import Optional +from typing import Dict, List, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import LocalCommInterface -from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName +from gwproto.enums import LocalCommInterface, MakeModel, TelemetryName class ElectricMeterCac(ComponentAttributeClass): diff --git a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py index 9d04eb20..33ae80e2 100644 --- a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py @@ -1,13 +1,9 @@ """MultipurposeSensorCac definition""" -from typing import Dict -from typing import List -from typing import Optional +from typing import Dict, List, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName -from gwproto.enums import Unit +from gwproto.enums import MakeModel, TelemetryName, Unit class MultipurposeSensorCac(ComponentAttributeClass): diff --git a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py index dec47efb..2b9b1eac 100644 --- a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py @@ -1,7 +1,6 @@ """PipeFlowSensorCac definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.enums import MakeModel diff --git a/src/gwproto/data_classes/cacs/relay_cac.py b/src/gwproto/data_classes/cacs/relay_cac.py index 0dd184d5..21aa57df 100644 --- a/src/gwproto/data_classes/cacs/relay_cac.py +++ b/src/gwproto/data_classes/cacs/relay_cac.py @@ -1,11 +1,9 @@ """RelayCac definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName +from gwproto.enums import MakeModel, TelemetryName class RelayCac(ComponentAttributeClass): diff --git a/src/gwproto/data_classes/cacs/resistive_heater_cac.py b/src/gwproto/data_classes/cacs/resistive_heater_cac.py index 818e67a7..94744439 100644 --- a/src/gwproto/data_classes/cacs/resistive_heater_cac.py +++ b/src/gwproto/data_classes/cacs/resistive_heater_cac.py @@ -1,7 +1,6 @@ """ElectricHeaterCac definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.enums import MakeModel diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index 3dbc43e7..4f0e76f8 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -1,12 +1,9 @@ """SimpleTempSensorCac definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName -from gwproto.enums import Unit +from gwproto.enums import MakeModel, TelemetryName, Unit class SimpleTempSensorCac(ComponentAttributeClass): diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 39c19bbe..3f8e7e4a 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -1,8 +1,7 @@ """ SCADA Component Class Definition """ from abc import ABC -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.data_classes.mixin import StreamlinedSerializerMixin diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 5b55516a..378d6005 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -1,8 +1,7 @@ """ ComponentAttributeClass""" from abc import ABC -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.mixin import StreamlinedSerializerMixin diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index 415dbbf8..833dccaa 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -1,14 +1,11 @@ """ElectricMeterComponent definition""" -from typing import Dict -from typing import List -from typing import Optional +from typing import Dict, List, Optional from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.component import Component from gwproto.enums import MakeModel -from gwproto.types import EgaugeIo -from gwproto.types import TelemetryReportingConfig +from gwproto.types import EgaugeIo, TelemetryReportingConfig class ElectricMeterComponent(Component): diff --git a/src/gwproto/data_classes/components/hubitat_component.py b/src/gwproto/data_classes/components/hubitat_component.py index 2b56d7e9..5b54ebbd 100644 --- a/src/gwproto/data_classes/components/hubitat_component.py +++ b/src/gwproto/data_classes/components/hubitat_component.py @@ -1,7 +1,6 @@ from typing import Optional import yarl - from gwproto.data_classes.component import Component from gwproto.types.hubitat_gt import HubitatGt diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index e569f399..574f2a52 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -1,14 +1,12 @@ from typing import Optional import yarl - from gwproto.data_classes.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode from gwproto.types.hubitat_component_gt import HubitatComponentGt from gwproto.types.hubitat_poller_gt import HubitatPollerGt -from gwproto.types.rest_poller_gt import RequestArgs -from gwproto.types.rest_poller_gt import RESTPollerSettings +from gwproto.types.rest_poller_gt import RequestArgs, RESTPollerSettings from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index a6c31ac4..b68eb33a 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -1,15 +1,18 @@ from typing import Optional import yarl - from gwproto.data_classes.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode -from gwproto.types.hubitat_component_gt import HubitatComponentGt -from gwproto.types.hubitat_component_gt import HubitatRESTResolutionSettings -from gwproto.types.hubitat_tank_gt import FibaroTempSensorSettings -from gwproto.types.hubitat_tank_gt import FibaroTempSensorSettingsGt -from gwproto.types.hubitat_tank_gt import HubitatTankSettingsGt +from gwproto.types.hubitat_component_gt import ( + HubitatComponentGt, + HubitatRESTResolutionSettings, +) +from gwproto.types.hubitat_tank_gt import ( + FibaroTempSensorSettings, + FibaroTempSensorSettingsGt, + HubitatTankSettingsGt, +) from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 191bfc4c..2dd8ffde 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -1,8 +1,6 @@ """MutlipurposeSensorComponent definition""" -from typing import Dict -from typing import List -from typing import Optional +from typing import Dict, List, Optional from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac from gwproto.data_classes.component import Component diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 3e097439..138c70a2 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -1,7 +1,6 @@ """PipeFlowSensorComponent definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac from gwproto.data_classes.component import Component diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 3a201d7a..32f04ead 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -1,7 +1,6 @@ """RelayComponent definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.cacs.relay_cac import RelayCac from gwproto.data_classes.component import Component diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index 383ee969..4cc2fc92 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -1,7 +1,6 @@ """ResistiveHeaterComponent definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac from gwproto.data_classes.component import Component diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index b2a498bc..049232dc 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -1,7 +1,6 @@ """SimpleTempSensorComponent definition""" -from typing import Dict -from typing import Optional +from typing import Dict, Optional from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac from gwproto.data_classes.component import Component diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 2e8a58d1..43a0031c 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -13,11 +13,7 @@ from dataclasses import dataclass from functools import cached_property from pathlib import Path -from typing import Any -from typing import List -from typing import Optional -from typing import Type -from typing import TypeVar +from typing import Any, List, Optional, Type, TypeVar from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.component import Component @@ -29,30 +25,31 @@ from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode from gwproto.data_classes.telemetry_tuple import TelemetryTuple -from gwproto.default_decoders import CacDecoder -from gwproto.default_decoders import ComponentDecoder -from gwproto.default_decoders import default_cac_decoder -from gwproto.default_decoders import default_component_decoder -from gwproto.enums import ActorClass -from gwproto.enums import Role -from gwproto.enums import TelemetryName -from gwproto.types import ElectricMeterCacGt_Maker -from gwproto.types import MultipurposeSensorCacGt_Maker -from gwproto.types import PipeFlowSensorCacGt_Maker -from gwproto.types import PipeFlowSensorComponentGt_Maker -from gwproto.types import RelayCacGt_Maker -from gwproto.types import RelayComponentGt_Maker -from gwproto.types import ResistiveHeaterCacGt_Maker -from gwproto.types import ResistiveHeaterComponentGt_Maker -from gwproto.types import SimpleTempSensorCacGt_Maker -from gwproto.types import SimpleTempSensorComponentGt_Maker -from gwproto.types import SpaceheatNodeGt_Maker +from gwproto.default_decoders import ( + CacDecoder, + ComponentDecoder, + default_cac_decoder, + default_component_decoder, +) +from gwproto.enums import ActorClass, Role, TelemetryName +from gwproto.types import ( + ElectricMeterCacGt_Maker, + MultipurposeSensorCacGt_Maker, + PipeFlowSensorCacGt_Maker, + PipeFlowSensorComponentGt_Maker, + RelayCacGt_Maker, + RelayComponentGt_Maker, + ResistiveHeaterCacGt_Maker, + ResistiveHeaterComponentGt_Maker, + SimpleTempSensorCacGt_Maker, + SimpleTempSensorComponentGt_Maker, + SpaceheatNodeGt_Maker, +) from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker from gwproto.types.multipurpose_sensor_component_gt import ( MultipurposeSensorComponentGt_Maker, ) - T = TypeVar("T") snake_add_underscore_to_camel_pattern = re.compile(r"(?-?\d+)/devices/(?P-?\d+).*?" ) diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 1a889daa..077f097d 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -2,23 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import List -from typing import Literal -from typing import Optional +from typing import Any, Dict, List, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.enums import TelemetryName -from gwproto.enums import Unit +from gwproto.enums import TelemetryName, Unit from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 6f7ecb82..36963556 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -2,23 +2,18 @@ import json import logging -from typing import Any -from typing import Dict -from typing import List -from typing import Literal -from typing import Optional +from typing import Any, Dict, List, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.components.multipurpose_sensor_component import ( MultipurposeSensorComponent, ) from gwproto.errors import SchemaError -from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig -from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig_Maker - +from gwproto.types.telemetry_reporting_config import ( + TelemetryReportingConfig, + TelemetryReportingConfig_Maker, +) LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index beb5a37b..cee374b2 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -2,20 +2,14 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac from gwproto.enums import MakeModel as EnumMakeModel from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index bcb39c27..423fc54c 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -2,21 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.components.pipe_flow_sensor_component import ( PipeFlowSensorComponent, ) from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 03f89a7e..0ddd2810 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -2,16 +2,12 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal +from typing import Any, Dict, Literal -from pydantic import BaseModel -from pydantic import Field +from pydantic import BaseModel, Field from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 6585d060..18963091 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -2,20 +2,14 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.cacs.relay_cac import RelayCac from gwproto.enums import MakeModel as EnumMakeModel from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 4668e1b6..62d868eb 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -2,19 +2,13 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.components.relay_component import RelayComponent from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 1727cede..0223f26b 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -2,20 +2,14 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac from gwproto.enums import MakeModel as EnumMakeModel from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 7bb00e79..7bd713a5 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -2,21 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterComponent, ) from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index ee385517..5ecc034b 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -1,7 +1,6 @@ import json import typing -from typing import Any -from typing import Literal +from typing import Any, Literal from gwproto.data_classes.cacs.rest_poller_cac import RESTPollerCac from gwproto.data_classes.component_attribute_class import ComponentAttributeClass diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index 23618258..95688065 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -5,9 +5,7 @@ import json import typing -from typing import Any -from typing import Literal -from typing import Optional +from typing import Any, Literal, Optional from gwproto.data_classes.component import Component from gwproto.data_classes.components.rest_poller_component import RESTPollerComponent diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index fbf45de1..4b61d5c6 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -4,15 +4,10 @@ """ from functools import cached_property -from typing import Literal -from typing import Optional -from typing import Tuple +from typing import Literal, Optional, Tuple import yarl -from pydantic import BaseModel -from pydantic import Extra -from pydantic import HttpUrl -from pydantic import root_validator +from pydantic import BaseModel, Extra, HttpUrl, root_validator from gwproto.utils import snake_to_camel diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index fec9dcb6..032d8d69 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -2,14 +2,9 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac from gwproto.enums import MakeModel as EnumMakeModel @@ -17,7 +12,6 @@ from gwproto.enums import Unit from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index a050fed8..85ffd3b1 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -2,21 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.components.simple_temp_sensor_component import ( SimpleTempSensorComponent, ) from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index b6b56e70..241819dc 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -2,18 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal +from typing import Any, Dict, Literal -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.errors import SchemaError -from gwproto.types.telemetry_snapshot_spaceheat import TelemetrySnapshotSpaceheat -from gwproto.types.telemetry_snapshot_spaceheat import TelemetrySnapshotSpaceheat_Maker - +from gwproto.types.telemetry_snapshot_spaceheat import ( + TelemetrySnapshotSpaceheat, + TelemetrySnapshotSpaceheat_Maker, +) LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 9045b2f1..b75f3362 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -2,21 +2,15 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.data_classes.sh_node import ShNode from gwproto.enums import ActorClass as EnumActorClass from gwproto.enums import Role as EnumRole from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index bf73fa92..c74a96b7 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -2,19 +2,12 @@ import json import logging -from typing import Any -from typing import Dict -from typing import List -from typing import Literal +from typing import Any, Dict, List, Literal -from pydantic import BaseModel -from pydantic import Field -from pydantic import validator +from pydantic import BaseModel, Field, validator from gwproto.errors import SchemaError -from gwproto.types.data_channel import DataChannel -from gwproto.types.data_channel import DataChannel_Maker - +from gwproto.types.data_channel import DataChannel, DataChannel_Maker LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 8a869304..74b28dce 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -2,21 +2,14 @@ import json import logging -from typing import Any -from typing import Dict -from typing import Literal -from typing import Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel -from pydantic import Field -from pydantic import root_validator -from pydantic import validator +from pydantic import BaseModel, Field, root_validator, validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.enums import Unit as EnumUnit from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 31276e36..1af6ae4b 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -2,20 +2,13 @@ import json import logging -from typing import Any -from typing import Dict -from typing import List -from typing import Literal +from typing import Any, Dict, List, Literal -from pydantic import BaseModel -from pydantic import Field -from pydantic import root_validator -from pydantic import validator +from pydantic import BaseModel, Field, root_validator, validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError - LOG_FORMAT = ( "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " "-35s %(lineno) -5d: %(message)s" diff --git a/src/gwproto/types/web_server_gt.py b/src/gwproto/types/web_server_gt.py index 36b015d8..62238ee0 100644 --- a/src/gwproto/types/web_server_gt.py +++ b/src/gwproto/types/web_server_gt.py @@ -1,9 +1,7 @@ -from pydantic import BaseModel -from pydantic import Extra +from pydantic import BaseModel, Extra from gwproto.utils import snake_to_camel - DEFAULT_WEB_SERVER_NAME = "default" diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index 591e66b4..bbf00dd5 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -6,7 +6,6 @@ import pytz - snake_add_underscore_to_camel_pattern = re.compile(r"(? None: diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index 830c0a55..8ec1e752 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import ComponentGt_Maker as Maker +from pydantic import ValidationError def test_component_gt_generated() -> None: diff --git a/tests/types/test_data_channel.py b/tests/types/test_data_channel.py index 867de68d..e1eadd17 100644 --- a/tests/types/test_data_channel.py +++ b/tests/types/test_data_channel.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import DataChannel_Maker as Maker +from pydantic import ValidationError def test_data_channel_generated() -> None: diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py index 7b34cc3f..0f7dfb77 100644 --- a/tests/types/test_egauge_io.py +++ b/tests/types/test_egauge_io.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import EgaugeIo_Maker as Maker +from pydantic import ValidationError def test_egauge_io_generated() -> None: diff --git a/tests/types/test_egauge_register_config.py b/tests/types/test_egauge_register_config.py index 39237ccb..96f710b8 100644 --- a/tests/types/test_egauge_register_config.py +++ b/tests/types/test_egauge_register_config.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import EgaugeRegisterConfig_Maker as Maker +from pydantic import ValidationError def test_egauge_register_config_generated() -> None: diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 2ff9ab97..ed550c93 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -3,12 +3,10 @@ import json import pytest -from pydantic import ValidationError - -from gwproto.enums import LocalCommInterface -from gwproto.enums import MakeModel +from gwproto.enums import LocalCommInterface, MakeModel from gwproto.errors import SchemaError from gwproto.types import ElectricMeterCacGt_Maker as Maker +from pydantic import ValidationError def test_electric_meter_cac_gt_generated() -> None: diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 4a147895..8bf8ed13 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -3,12 +3,11 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types.electric_meter_component_gt import ( ElectricMeterComponentGt_Maker as Maker, ) +from pydantic import ValidationError def test_electric_meter_component_gt_generated() -> None: diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py index cd659806..c98a7e68 100644 --- a/tests/types/test_gt_dispatch_boolean.py +++ b/tests/types/test_gt_dispatch_boolean.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtDispatchBoolean_Maker as Maker +from pydantic import ValidationError def test_gt_dispatch_boolean_generated() -> None: diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py index 2371adce..072df8f7 100644 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ b/tests/types/test_gt_dispatch_boolean_local.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtDispatchBooleanLocal_Maker as Maker +from pydantic import ValidationError def test_gt_dispatch_boolean_local_generated() -> None: diff --git a/tests/types/test_gt_driver_booleanactuator_cmd.py b/tests/types/test_gt_driver_booleanactuator_cmd.py index 8568830e..07693ca2 100644 --- a/tests/types/test_gt_driver_booleanactuator_cmd.py +++ b/tests/types/test_gt_driver_booleanactuator_cmd.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtDriverBooleanactuatorCmd_Maker as Maker +from pydantic import ValidationError def test_gt_driver_booleanactuator_cmd_generated() -> None: diff --git a/tests/types/test_gt_sh_booleanactuator_cmd_status.py b/tests/types/test_gt_sh_booleanactuator_cmd_status.py index 50a883fe..a5aa7d8a 100644 --- a/tests/types/test_gt_sh_booleanactuator_cmd_status.py +++ b/tests/types/test_gt_sh_booleanactuator_cmd_status.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtShBooleanactuatorCmdStatus_Maker as Maker +from pydantic import ValidationError def test_gt_sh_booleanactuator_cmd_status_generated() -> None: diff --git a/tests/types/test_gt_sh_cli_atn_cmd.py b/tests/types/test_gt_sh_cli_atn_cmd.py index 47ac29b5..7a423d5c 100644 --- a/tests/types/test_gt_sh_cli_atn_cmd.py +++ b/tests/types/test_gt_sh_cli_atn_cmd.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtShCliAtnCmd_Maker as Maker +from pydantic import ValidationError def test_gt_sh_cli_atn_cmd_generated() -> None: diff --git a/tests/types/test_gt_sh_multipurpose_telemetry_status.py b/tests/types/test_gt_sh_multipurpose_telemetry_status.py index 1645b626..4f2fa6e7 100644 --- a/tests/types/test_gt_sh_multipurpose_telemetry_status.py +++ b/tests/types/test_gt_sh_multipurpose_telemetry_status.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtShMultipurposeTelemetryStatus_Maker as Maker +from pydantic import ValidationError def test_gt_sh_multipurpose_telemetry_status_generated() -> None: diff --git a/tests/types/test_gt_sh_simple_telemetry_status.py b/tests/types/test_gt_sh_simple_telemetry_status.py index 3d802472..ee94a6d2 100644 --- a/tests/types/test_gt_sh_simple_telemetry_status.py +++ b/tests/types/test_gt_sh_simple_telemetry_status.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtShSimpleTelemetryStatus_Maker as Maker +from pydantic import ValidationError def test_gt_sh_simple_telemetry_status_generated() -> None: diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index c4ba62f4..6c4efdc2 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtShStatus_Maker as Maker +from pydantic import ValidationError def test_gt_sh_status_generated() -> None: diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index 8419ee69..adbdd693 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import GtShTelemetryFromMultipurposeSensor_Maker as Maker +from pydantic import ValidationError def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: diff --git a/tests/types/test_gt_telemetry.py b/tests/types/test_gt_telemetry.py index 729793f1..ff21d985 100644 --- a/tests/types/test_gt_telemetry.py +++ b/tests/types/test_gt_telemetry.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtTelemetry_Maker as Maker +from pydantic import ValidationError def test_gt_telemetry_generated() -> None: diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index bc358a63..b1bb0e1c 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import HeartbeatB_Maker as Maker +from pydantic import ValidationError def test_heartbeat_b_generated() -> None: diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index c05d972a..4b7c47e0 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -1,7 +1,6 @@ """Test HubitatGt""" import yarl - from gwproto.types.hubitat_gt import HubitatGt diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 08a61023..f356edf4 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -3,12 +3,10 @@ import json import pytest -from pydantic import ValidationError - -from gwproto.enums import MakeModel -from gwproto.enums import Unit +from gwproto.enums import MakeModel, Unit from gwproto.errors import SchemaError from gwproto.types import MultipurposeSensorCacGt_Maker as Maker +from pydantic import ValidationError def test_multipurpose_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index 92dbe8cc..eaccc910 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -3,12 +3,11 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types.multipurpose_sensor_component_gt import ( MultipurposeSensorComponentGt_Maker as Maker, ) +from pydantic import ValidationError def test_multipurpose_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index 0049e76c..df5160f2 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import PipeFlowSensorCacGt_Maker as Maker +from pydantic import ValidationError def test_pipe_flow_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index 7af9f5fe..f92c64ba 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import PipeFlowSensorComponentGt_Maker as Maker +from pydantic import ValidationError def test_pipe_flow_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_power_watts.py b/tests/types/test_power_watts.py index 09e18d94..6a66f7b4 100644 --- a/tests/types/test_power_watts.py +++ b/tests/types/test_power_watts.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import PowerWatts_Maker as Maker +from pydantic import ValidationError def test_power_watts_generated() -> None: diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index 5ca52fe8..3311cc61 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import RelayCacGt_Maker as Maker +from pydantic import ValidationError def test_relay_cac_gt_generated() -> None: diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index 40f3b1ea..cec3d0b0 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import RelayComponentGt_Maker as Maker +from pydantic import ValidationError def test_relay_component_gt_generated() -> None: diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 655472fa..d3dc57f5 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError - from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import ResistiveHeaterCacGt_Maker as Maker +from pydantic import ValidationError def test_resistive_heater_cac_gt_generated() -> None: diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index ec46745a..843f6bf9 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import ResistiveHeaterComponentGt_Maker as Maker +from pydantic import ValidationError def test_resistive_heater_component_gt_generated() -> None: diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index 6a47a36d..24b34c3e 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -3,13 +3,10 @@ import json import pytest -from pydantic import ValidationError - -from gwproto.enums import MakeModel -from gwproto.enums import TelemetryName -from gwproto.enums import Unit +from gwproto.enums import MakeModel, TelemetryName, Unit from gwproto.errors import SchemaError from gwproto.types import SimpleTempSensorCacGt_Maker as Maker +from pydantic import ValidationError def test_simple_temp_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_simple_temp_sensor_component_gt.py b/tests/types/test_simple_temp_sensor_component_gt.py index 4ac0d266..1c871d9e 100644 --- a/tests/types/test_simple_temp_sensor_component_gt.py +++ b/tests/types/test_simple_temp_sensor_component_gt.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import SimpleTempSensorComponentGt_Maker as Maker +from pydantic import ValidationError def test_simple_temp_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_snapshot_spaceheat.py b/tests/types/test_snapshot_spaceheat.py index 70600d1a..5723f7aa 100644 --- a/tests/types/test_snapshot_spaceheat.py +++ b/tests/types/test_snapshot_spaceheat.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import SnapshotSpaceheat_Maker as Maker +from pydantic import ValidationError def test_snapshot_spaceheat_generated() -> None: diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 8e657c32..7122cde7 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -3,12 +3,10 @@ import json import pytest -from pydantic import ValidationError - -from gwproto.enums import ActorClass -from gwproto.enums import Role +from gwproto.enums import ActorClass, Role from gwproto.errors import SchemaError from gwproto.types import SpaceheatNodeGt_Maker as Maker +from pydantic import ValidationError def test_spaceheat_node_gt_generated() -> None: diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py index d373c077..2ffca99e 100644 --- a/tests/types/test_ta_data_channels.py +++ b/tests/types/test_ta_data_channels.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import TaDataChannels_Maker as Maker +from pydantic import ValidationError def test_ta_data_channels_generated() -> None: diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index 3faa6bbf..9783633e 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -3,12 +3,10 @@ import json import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName -from gwproto.enums import Unit +from gwproto.enums import TelemetryName, Unit from gwproto.errors import SchemaError from gwproto.types import TelemetryReportingConfig_Maker as Maker +from pydantic import ValidationError def test_telemetry_reporting_config_generated() -> None: diff --git a/tests/types/test_telemetry_snapshot_spaceheat.py b/tests/types/test_telemetry_snapshot_spaceheat.py index 9ecfd31d..18de6f73 100644 --- a/tests/types/test_telemetry_snapshot_spaceheat.py +++ b/tests/types/test_telemetry_snapshot_spaceheat.py @@ -3,10 +3,9 @@ import json import pytest -from pydantic import ValidationError - from gwproto.errors import SchemaError from gwproto.types import TelemetrySnapshotSpaceheat_Maker as Maker +from pydantic import ValidationError def test_telemetry_snapshot_spaceheat_generated() -> None: diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 4bb3fa8b..458bc5a2 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1,31 +1,24 @@ from gwproto.data_classes.component import Component from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.data_classes.components.electric_meter_component import ElectricMeterCac from gwproto.data_classes.components.electric_meter_component import ( + ElectricMeterCac, ElectricMeterComponent, ) from gwproto.data_classes.components.multipurpose_sensor_component import ( MultipurposeSensorCac, -) -from gwproto.data_classes.components.multipurpose_sensor_component import ( MultipurposeSensorComponent, ) -from gwproto.data_classes.components.pipe_flow_sensor_component import PipeFlowSensorCac from gwproto.data_classes.components.pipe_flow_sensor_component import ( + PipeFlowSensorCac, PipeFlowSensorComponent, ) -from gwproto.data_classes.components.relay_component import RelayCac -from gwproto.data_classes.components.relay_component import RelayComponent +from gwproto.data_classes.components.relay_component import RelayCac, RelayComponent from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterCac, -) -from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterComponent, ) from gwproto.data_classes.components.simple_temp_sensor_component import ( SimpleTempSensorCac, -) -from gwproto.data_classes.components.simple_temp_sensor_component import ( SimpleTempSensorComponent, ) from gwproto.data_classes.sh_node import ShNode From 1c7782eea9ce7e18478b9d2498d1b3a9b563de1b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 16:07:49 -0400 Subject: [PATCH 032/168] ruff format --- src/gwproto/data_classes/component.py | 2 +- .../data_classes/component_attribute_class.py | 2 +- src/gwproto/data_classes/hardware_layout.py | 14 +- src/gwproto/enums/__init__.py | 15 +- src/gwproto/enums/unit.py | 2 +- src/gwproto/gs/__init__.py | 2 +- src/gwproto/gs/gs_dispatch.py | 3 +- src/gwproto/gs/gs_dispatch_base.py | 1 + src/gwproto/gs/gs_dispatch_maker.py | 1 + src/gwproto/gs/gs_pwr.py | 3 +- src/gwproto/gs/gs_pwr_base.py | 1 + src/gwproto/gs/gs_pwr_maker.py | 1 + src/gwproto/types/__init__.py | 2 +- tests/test_misc/test_flush_and_load_house.py | 978 +++++++++--------- 14 files changed, 514 insertions(+), 513 deletions(-) diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 3f8e7e4a..4a932973 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -1,4 +1,4 @@ -""" SCADA Component Class Definition """ +"""SCADA Component Class Definition""" from abc import ABC from typing import Dict, Optional diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 378d6005..1e387e75 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -1,4 +1,4 @@ -""" ComponentAttributeClass""" +"""ComponentAttributeClass""" from abc import ABC from typing import Dict, Optional diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 43a0031c..c424e8b5 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -85,10 +85,8 @@ def load_cacs( ]: for d in layout.get(type_name, []): try: - cacs[d["ComponentAttributeClassId"]] = ( - maker_class.dict_to_dc( # type:ignore[attr-defined] - d - ) + cacs[d["ComponentAttributeClassId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] + d ) except Exception as e: if raise_errors: @@ -132,10 +130,8 @@ def load_components( ]: for d in layout.get(type_name, []): try: - components[d["ComponentId"]] = ( - maker_class.dict_to_dc( # type:ignore[attr-defined] - d - ) + components[d["ComponentId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] + d ) except Exception as e: if raise_errors: @@ -331,7 +327,7 @@ def get_components_by_type(self, type_: Type[T]) -> list[T]: for i, entry in enumerate(entries): if not isinstance(entry, type_): raise ValueError( - f"ERROR. Entry {i+1} in " + f"ERROR. Entry {i + 1} in " f"HardwareLayout.components_by_typ[{type_}] " f"has the wrong type {type(entry)}" ) diff --git a/src/gwproto/enums/__init__.py b/src/gwproto/enums/__init__.py index fc4a4177..81089efa 100644 --- a/src/gwproto/enums/__init__.py +++ b/src/gwproto/enums/__init__.py @@ -30,7 +30,8 @@ - [ASLs](https://gridwork-type-registry.readthedocs.io/en/latest/asls.html) - """ +""" + from gwproto.enums.actor_class import ActorClass from gwproto.enums.local_comm_interface import LocalCommInterface from gwproto.enums.make_model import MakeModel @@ -39,10 +40,10 @@ from gwproto.enums.unit import Unit __all__ = [ - "ActorClass", # [sh.actor.class.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shactorclass) - "LocalCommInterface", # [local.comm.interface.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#localcomminterface) - "MakeModel", # [spaceheat.make.model.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel) - "Role", # [sh.node.role.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole) - "TelemetryName", # [spaceheat.telemetry.name.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname) - "Unit", # [spaceheat.unit.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit) + "ActorClass", # [sh.actor.class.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shactorclass) + "LocalCommInterface", # [local.comm.interface.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#localcomminterface) + "MakeModel", # [spaceheat.make.model.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel) + "Role", # [sh.node.role.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole) + "TelemetryName", # [spaceheat.telemetry.name.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname) + "Unit", # [spaceheat.unit.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit) ] diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 4a8251d2..66b7a02b 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -174,5 +174,5 @@ def symbols(cls) -> List[str]: "AmpsRms": "000", "VoltsRms": "000", "Gallons": "000", - "ThermostatStateEnum": "000" + "ThermostatStateEnum": "000", } diff --git a/src/gwproto/gs/__init__.py b/src/gwproto/gs/__init__.py index 592b6f54..0ef22042 100644 --- a/src/gwproto/gs/__init__.py +++ b/src/gwproto/gs/__init__.py @@ -6,4 +6,4 @@ "GsDispatch_Maker", "GsPwr", "GsPwr_Maker", -] \ No newline at end of file +] diff --git a/src/gwproto/gs/gs_dispatch.py b/src/gwproto/gs/gs_dispatch.py index 253a5fb4..f032c30d 100644 --- a/src/gwproto/gs/gs_dispatch.py +++ b/src/gwproto/gs/gs_dispatch.py @@ -1,4 +1,5 @@ """GridWorks serial message protocol GsPwr100 with MpAlias d""" + from gwproto.errors import SchemaError from gwproto.gs.gs_dispatch_base import GsDispatchBase @@ -9,5 +10,5 @@ def check_for_errors(self): if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - def hand_coded_errors(self): # noqa + def hand_coded_errors(self): # noqa return [] diff --git a/src/gwproto/gs/gs_dispatch_base.py b/src/gwproto/gs/gs_dispatch_base.py index 0d6a6ca0..bbba2894 100644 --- a/src/gwproto/gs/gs_dispatch_base.py +++ b/src/gwproto/gs/gs_dispatch_base.py @@ -1,4 +1,5 @@ """Base for GridWorks gwproto gs.dispatch.100 with TypeName d""" + import struct from typing import List, NamedTuple diff --git a/src/gwproto/gs/gs_dispatch_maker.py b/src/gwproto/gs/gs_dispatch_maker.py index f8836ee8..0a11b7da 100644 --- a/src/gwproto/gs/gs_dispatch_maker.py +++ b/src/gwproto/gs/gs_dispatch_maker.py @@ -1,4 +1,5 @@ """Makes GridWorksSerial protocol GsDispatch with MpAlias d""" + import struct from gwproto.gs.gs_dispatch import GsDispatch diff --git a/src/gwproto/gs/gs_pwr.py b/src/gwproto/gs/gs_pwr.py index 36a8d17b..bea54761 100644 --- a/src/gwproto/gs/gs_pwr.py +++ b/src/gwproto/gs/gs_pwr.py @@ -1,4 +1,5 @@ """GridWorks serial message protocol gs.pwr.100 with MpAlias p""" + from gwproto.errors import SchemaError from gwproto.gs.gs_pwr_base import GsPwrBase @@ -9,5 +10,5 @@ def check_for_errors(self): if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - def hand_coded_errors(self): # noqa + def hand_coded_errors(self): # noqa return [] diff --git a/src/gwproto/gs/gs_pwr_base.py b/src/gwproto/gs/gs_pwr_base.py index 3255c33d..ad6dbec6 100644 --- a/src/gwproto/gs/gs_pwr_base.py +++ b/src/gwproto/gs/gs_pwr_base.py @@ -1,4 +1,5 @@ """Base for GridWorks gwproto gs.pwr.100 with TypeName p""" + import struct from typing import List, NamedTuple diff --git a/src/gwproto/gs/gs_pwr_maker.py b/src/gwproto/gs/gs_pwr_maker.py index cabf4524..dcf46fe5 100644 --- a/src/gwproto/gs/gs_pwr_maker.py +++ b/src/gwproto/gs/gs_pwr_maker.py @@ -1,4 +1,5 @@ """Makes GridWorksSerial protocol gs.pwr.100 with MpAlias p""" + import struct from gwproto.gs.gs_pwr import GsPwr diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index e634d73d..158d95da 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -1,4 +1,4 @@ -""" List of all the types """ +"""List of all the types""" from gwproto.types.component_attribute_class_gt import ( ComponentAttributeClassGt, diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index b5136a50..218161f9 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -94,495 +94,493 @@ def test_flush_and_load_house(): def test_load_real_house(): - layout = HardwareLayout( - { - "MyAtomicTNodeGNode": { - "GNodeId": "d636cbeb-c4ad-45cd-b5bc-1c64cc33f4f4", - "Alias": "w.isone.ct.newhaven.orange1", - "DisplayName": "Little Orange House Garage Heating System AtomicTNode", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "AtomicTNode", - }, - "MyTerminalAssetGNode": { - "GNodeId": "137d7f06-ea65-4254-bfd1-5d56fa789229", - "Alias": "w.isone.ct.newhaven.orange1.ta", - "DisplayName": "Little Orange House Garage Heating System TerminalAsset", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "TerminalAsset", - }, - "MyScadaGNode": { - "GNodeId": "28817671-3899-4e24-a337-abcb8633e47a", - "Alias": "w.isone.ct.newhaven.orange1.scada", - "DisplayName": "Little Orange House Garage Heating System SCADA", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "Scada", - }, - "ShNodes": [ - { - "Alias": "a", - "RoleGtEnumSymbol": "6ddff83b", - "ActorClassGtEnumSymbol": "b103058f", - "DisplayName": "AtomicTNode", - "ShNodeId": "7a4fe194-f572-407e-ab65-8d38f83d9eb0", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.home", - "RoleGtEnumSymbol": "863e50d1", - "ActorClassGtEnumSymbol": "32d3d19f", - "DisplayName": "Little Orange House HomeAlone", - "ShNodeId": "a1537de0-5c83-422e-9826-0995e0419953", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.s", - "RoleGtEnumSymbol": "d0afb424", - "ActorClassGtEnumSymbol": "6d37aa41", - "DisplayName": "Little Orange House Main Scada", - "ShNodeId": "19fc828e-0f9f-4a15-819b-6f02e38500c7", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1", - "RoleGtEnumSymbol": "99c5f326", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First 4.5 kW boost in tank", - "ShNodeId": "164d6c73-7c0c-4063-8cf9-01cde3a32b7c", - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "RatedVoltageV": 240, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "30A Relay for first boost element", - "ShNodeId": "a9c94a2f-1800-4394-a90f-4f50dba053ac", - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank", - "RoleGtEnumSymbol": "3ecfe9b8", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Test Axeman Tank", - "ShNodeId": "4ac89701-3472-4fb2-b404-8e3012af0399", - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.m", - "RoleGtEnumSymbol": "9ac68b6e", - "ActorClassGtEnumSymbol": "2ea112b9", - "DisplayName": "Main Power Meter Little Orange House Test System", - "ShNodeId": "55cecbdb-8a47-4160-a1d6-f3617a6279b4", - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe Out of Tank", - "ShNodeId": "1348b9cc-d45e-4095-aa39-24abb58a7498", - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe into Tank", - "ShNodeId": "2aeb26f7-8251-431c-83b5-0ad3c0e4cff0", - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump", - "RoleGtEnumSymbol": "b0eaf2ba", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Circulator Pump", - "ShNodeId": "4dcf1be8-35ea-48c5-a7a0-9d74476b5a8d", - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Circulator Pump Relay", - "ShNodeId": "d6109c64-daec-49b1-8b4f-1c69ab012e69", - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1", - "RoleGtEnumSymbol": "05fdd645", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Single baseboard radiator", - "ShNodeId": "9d8641f7-f5e2-4683-a427-5ebb18345b89", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan", - "RoleGtEnumSymbol": "6896109b", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First baseboard radiator fan", - "ShNodeId": "57b3632f-df3e-4a7f-9205-1e6f953dece9", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Relay for first baseboard radiator fan", - "ShNodeId": "0223a903-ae99-4cd9-bd67-f28e1e799938", - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage", - "RoleGtEnumSymbol": "65725f44", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Garage", - "ShNodeId": "e06809dc-7915-4389-9559-d4a89c3c4994", - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors", - "RoleGtEnumSymbol": "dd975b31", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "43 Avon St Microclimate", - "ShNodeId": "7706f7ae-d01e-4ec2-89f4-8eb6fcc64f18", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.flowmeter1", - "RoleGtEnumSymbol": "ece3b600", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Flow Meter on distribution pipe out of tank", - "ShNodeId": "0cb98277-b4b5-4016-8afa-ed2bffca6750", - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe out of tank", - "ShNodeId": "88b30858-65c0-470b-82e5-0981d3f2b5fe", - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe into tank", - "ShNodeId": "675458cf-2da8-4792-8c50-e04c3f2f8326", - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.temp0", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Tank temp sensor temp0 (on top)", - "ShNodeId": "24e6c994-ff61-43b2-8550-d2017f413cb3", - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage.temp1", - "RoleGtEnumSymbol": "fec74958", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Garage Temp sensor", - "ShNodeId": "12b072ef-0a8b-4569-a055-12b486f35f04", - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors.temp1", - "RoleGtEnumSymbol": "5938bf1f", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Outdoor Temp sensor", - "ShNodeId": "f0821873-e34f-4d53-a69a-10f6bed6d8d8", - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - ], - "ThermalEdges": [ - {"FromNodeAlias": "a.elt1", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank.in", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank", "ToNodeAlias": "a.tank.out"}, - {"FromNodeAlias": "a.tank.out", "ToNodeAlias": "a.tank.out.pump"}, - { - "FromNodeAlias": "a.tank.out.pump", - "ToNodeAlias": "a.tank.out.pump.baseboard1", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.in", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.out.pump.baseboard1.garage", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1.garage", - "ToNodeAlias": "a.outdoors", - }, - ], - "ResistiveHeaterComponents": [ - { - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "DisplayName": "First 4.5 kW boost in tank", - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "TypeName": "resistive.heater.component.gt", - "Version": "000", - "MaxPowerW": 4500, - } - ], - "RelayComponents": [ - { - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "DisplayName": "relay for radiator fan", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 1, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "DisplayName": "relay for first elt in tank", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 2, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "DisplayName": "relay for main circulator pump", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 3, - "NormallyOpen": False, - "TypeName": "relay.component.gt", - "Version": "000", - }, - ], - "MultipurposeSensorComponents": [], - "SimpleTempSensorComponents": [ - { - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "DisplayName": "Outdoor Temperature Sensor", - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "DisplayName": "Temp sensor on pipe out of tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00033ffe", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "DisplayName": "Temp sensor on pipe into tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "000363a9", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "DisplayName": "Component for a.tank.temp0 (on top)", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00041d3f", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "DisplayName": "First garage temp sensor", - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - ], - "ElectricMeterComponents": [ - { - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "DisplayName": "Main power meter for Little orange house garage space heat", - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "HwUid": "1001ab", - "TypeName": "electric.meter.component.gt", - "Version": "000", - } - ], - "PipeFlowSensorComponents": [ - { - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "DisplayName": "Flow meter on pipe out of tank", - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "TypeName": "pipe.flow.sensor.component.gt", - "Version": "000", - } - ], - "OtherComponents": [ - { - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - }, - { - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "DisplayName": "Hydronic pipe out of tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "DisplayName": "Hydronic pipe into tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "DisplayName": "Circulator Pump", - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - }, - { - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "DisplayName": "Little Orange House garage", - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - }, - ], - "ResistiveHeaterCacs": [ - { - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModelGtEnumSymbol": "00000000", - "DisplayName": "Orange Garage heating element", - "NameplateMaxPowerW": 4500, - "RatedVoltageV": 240, - "TypeName": "resistive.heater.cac.gt", - "Version": "000", - } - ], - "RelayCacs": [ - { - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "MakeModelGtEnumSymbol": "fabfa505", - "TypicalResponseTimeMs": 200, - "TypeName": "relay.cac.gt", - "Version": "000", - } - ], - "PipeFlowSensorCacs": [ - { - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModelGtEnumSymbol": "00000000", - "TypeName": "pipe.flow.sensor.cac.gt", - "Version": "000", - } - ], - "ElectricMeterCacs": [ - { - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "MakeModelGtEnumSymbol": "d300635e", - "DisplayName": "Schneider Electric Iem3455 Power Meter", - "InterfaceGtEnumSymbol": "a6a4ac9f", - "PollPeriodMs": 1000, - "DefaultBaud": 9600, - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - ], - "MultipurposeSensorCacs": [], - "SimpleTempSensorCacs": [ - { - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "MakeModelGtEnumSymbol": "acd93fb3", - "DisplayName": "Adafruit High Temp Waterproof DS18B20 Digital Temp Sensor", - "CommsMethod": "OneWire", - "Exponent": -3, - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TempUnitGtEnumSymbol": "ec14bd47", - "TypicalResponseTimeMs": 880, - "TypeName": "simple.temp.sensor.cac.gt", - "Versoin": "000", - }, - { - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - { - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - ], - "OtherCacs": [ - { - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "MakeModelGtEnumSymbol": "00000000", - }, - ], - } - ) + layout = HardwareLayout({ + "MyAtomicTNodeGNode": { + "GNodeId": "d636cbeb-c4ad-45cd-b5bc-1c64cc33f4f4", + "Alias": "w.isone.ct.newhaven.orange1", + "DisplayName": "Little Orange House Garage Heating System AtomicTNode", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "AtomicTNode", + }, + "MyTerminalAssetGNode": { + "GNodeId": "137d7f06-ea65-4254-bfd1-5d56fa789229", + "Alias": "w.isone.ct.newhaven.orange1.ta", + "DisplayName": "Little Orange House Garage Heating System TerminalAsset", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "TerminalAsset", + }, + "MyScadaGNode": { + "GNodeId": "28817671-3899-4e24-a337-abcb8633e47a", + "Alias": "w.isone.ct.newhaven.orange1.scada", + "DisplayName": "Little Orange House Garage Heating System SCADA", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "Scada", + }, + "ShNodes": [ + { + "Alias": "a", + "RoleGtEnumSymbol": "6ddff83b", + "ActorClassGtEnumSymbol": "b103058f", + "DisplayName": "AtomicTNode", + "ShNodeId": "7a4fe194-f572-407e-ab65-8d38f83d9eb0", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.home", + "RoleGtEnumSymbol": "863e50d1", + "ActorClassGtEnumSymbol": "32d3d19f", + "DisplayName": "Little Orange House HomeAlone", + "ShNodeId": "a1537de0-5c83-422e-9826-0995e0419953", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.s", + "RoleGtEnumSymbol": "d0afb424", + "ActorClassGtEnumSymbol": "6d37aa41", + "DisplayName": "Little Orange House Main Scada", + "ShNodeId": "19fc828e-0f9f-4a15-819b-6f02e38500c7", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.elt1", + "RoleGtEnumSymbol": "99c5f326", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First 4.5 kW boost in tank", + "ShNodeId": "164d6c73-7c0c-4063-8cf9-01cde3a32b7c", + "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", + "RatedVoltageV": 240, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.elt1.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "30A Relay for first boost element", + "ShNodeId": "a9c94a2f-1800-4394-a90f-4f50dba053ac", + "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank", + "RoleGtEnumSymbol": "3ecfe9b8", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Little Orange House Test Axeman Tank", + "ShNodeId": "4ac89701-3472-4fb2-b404-8e3012af0399", + "ComponentId": "780788df-9706-4299-b116-304a48838338", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.m", + "RoleGtEnumSymbol": "9ac68b6e", + "ActorClassGtEnumSymbol": "2ea112b9", + "DisplayName": "Main Power Meter Little Orange House Test System", + "ShNodeId": "55cecbdb-8a47-4160-a1d6-f3617a6279b4", + "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out", + "RoleGtEnumSymbol": "fe3cbdd5", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Main Heat Distribution Pipe Out of Tank", + "ShNodeId": "1348b9cc-d45e-4095-aa39-24abb58a7498", + "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.in", + "RoleGtEnumSymbol": "fe3cbdd5", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Main Heat Distribution Pipe into Tank", + "ShNodeId": "2aeb26f7-8251-431c-83b5-0ad3c0e4cff0", + "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump", + "RoleGtEnumSymbol": "b0eaf2ba", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Circulator Pump", + "ShNodeId": "4dcf1be8-35ea-48c5-a7a0-9d74476b5a8d", + "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "Circulator Pump Relay", + "ShNodeId": "d6109c64-daec-49b1-8b4f-1c69ab012e69", + "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1", + "RoleGtEnumSymbol": "05fdd645", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Single baseboard radiator", + "ShNodeId": "9d8641f7-f5e2-4683-a427-5ebb18345b89", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.fan", + "RoleGtEnumSymbol": "6896109b", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First baseboard radiator fan", + "ShNodeId": "57b3632f-df3e-4a7f-9205-1e6f953dece9", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.fan.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "Relay for first baseboard radiator fan", + "ShNodeId": "0223a903-ae99-4cd9-bd67-f28e1e799938", + "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.garage", + "RoleGtEnumSymbol": "65725f44", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Little Orange House Garage", + "ShNodeId": "e06809dc-7915-4389-9559-d4a89c3c4994", + "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.outdoors", + "RoleGtEnumSymbol": "dd975b31", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "43 Avon St Microclimate", + "ShNodeId": "7706f7ae-d01e-4ec2-89f4-8eb6fcc64f18", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.flowmeter1", + "RoleGtEnumSymbol": "ece3b600", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Flow Meter on distribution pipe out of tank", + "ShNodeId": "0cb98277-b4b5-4016-8afa-ed2bffca6750", + "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.temp1", + "RoleGtEnumSymbol": "c480f612", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Temp Sensor on distribution pipe out of tank", + "ShNodeId": "88b30858-65c0-470b-82e5-0981d3f2b5fe", + "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.in.temp1", + "RoleGtEnumSymbol": "c480f612", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Temp Sensor on distribution pipe into tank", + "ShNodeId": "675458cf-2da8-4792-8c50-e04c3f2f8326", + "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.temp0", + "RoleGtEnumSymbol": "73308a1f", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Tank temp sensor temp0 (on top)", + "ShNodeId": "24e6c994-ff61-43b2-8550-d2017f413cb3", + "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.garage.temp1", + "RoleGtEnumSymbol": "fec74958", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First Garage Temp sensor", + "ShNodeId": "12b072ef-0a8b-4569-a055-12b486f35f04", + "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", + "ReportingSamplePeriodS": 60, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.outdoors.temp1", + "RoleGtEnumSymbol": "5938bf1f", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First Outdoor Temp sensor", + "ShNodeId": "f0821873-e34f-4d53-a69a-10f6bed6d8d8", + "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", + "ReportingSamplePeriodS": 60, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + ], + "ThermalEdges": [ + {"FromNodeAlias": "a.elt1", "ToNodeAlias": "a.tank"}, + {"FromNodeAlias": "a.tank.in", "ToNodeAlias": "a.tank"}, + {"FromNodeAlias": "a.tank", "ToNodeAlias": "a.tank.out"}, + {"FromNodeAlias": "a.tank.out", "ToNodeAlias": "a.tank.out.pump"}, + { + "FromNodeAlias": "a.tank.out.pump", + "ToNodeAlias": "a.tank.out.pump.baseboard1", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1", + "ToNodeAlias": "a.tank.in", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1", + "ToNodeAlias": "a.tank.out.pump.baseboard1.garage", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1.garage", + "ToNodeAlias": "a.outdoors", + }, + ], + "ResistiveHeaterComponents": [ + { + "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", + "DisplayName": "First 4.5 kW boost in tank", + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "TypeName": "resistive.heater.component.gt", + "Version": "000", + "MaxPowerW": 4500, + } + ], + "RelayComponents": [ + { + "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", + "DisplayName": "relay for radiator fan", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 1, + "NormallyOpen": True, + "TypeName": "relay.component.gt", + "Version": "000", + }, + { + "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", + "DisplayName": "relay for first elt in tank", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 2, + "NormallyOpen": True, + "TypeName": "relay.component.gt", + "Version": "000", + }, + { + "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", + "DisplayName": "relay for main circulator pump", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 3, + "NormallyOpen": False, + "TypeName": "relay.component.gt", + "Version": "000", + }, + ], + "MultipurposeSensorComponents": [], + "SimpleTempSensorComponents": [ + { + "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", + "DisplayName": "Outdoor Temperature Sensor", + "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", + "DisplayName": "Temp sensor on pipe out of tank", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "00033ffe", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", + "DisplayName": "Temp sensor on pipe into tank", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "000363a9", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", + "DisplayName": "Component for a.tank.temp0 (on top)", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "00041d3f", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", + "DisplayName": "First garage temp sensor", + "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + ], + "ElectricMeterComponents": [ + { + "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", + "DisplayName": "Main power meter for Little orange house garage space heat", + "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", + "HwUid": "1001ab", + "TypeName": "electric.meter.component.gt", + "Version": "000", + } + ], + "PipeFlowSensorComponents": [ + { + "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", + "DisplayName": "Flow meter on pipe out of tank", + "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", + "TypeName": "pipe.flow.sensor.component.gt", + "Version": "000", + } + ], + "OtherComponents": [ + { + "ComponentId": "780788df-9706-4299-b116-304a48838338", + "DisplayName": "Little Orange house Axeman Tank", + "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", + }, + { + "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", + "DisplayName": "Hydronic pipe out of tank", + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + }, + { + "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", + "DisplayName": "Hydronic pipe into tank", + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + }, + { + "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", + "DisplayName": "Circulator Pump", + "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", + }, + { + "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", + "DisplayName": "Little Orange House garage", + "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", + }, + ], + "ResistiveHeaterCacs": [ + { + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "MakeModelGtEnumSymbol": "00000000", + "DisplayName": "Orange Garage heating element", + "NameplateMaxPowerW": 4500, + "RatedVoltageV": 240, + "TypeName": "resistive.heater.cac.gt", + "Version": "000", + } + ], + "RelayCacs": [ + { + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "MakeModelGtEnumSymbol": "fabfa505", + "TypicalResponseTimeMs": 200, + "TypeName": "relay.cac.gt", + "Version": "000", + } + ], + "PipeFlowSensorCacs": [ + { + "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", + "MakeModelGtEnumSymbol": "00000000", + "TypeName": "pipe.flow.sensor.cac.gt", + "Version": "000", + } + ], + "ElectricMeterCacs": [ + { + "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", + "MakeModelGtEnumSymbol": "d300635e", + "DisplayName": "Schneider Electric Iem3455 Power Meter", + "InterfaceGtEnumSymbol": "a6a4ac9f", + "PollPeriodMs": 1000, + "DefaultBaud": 9600, + "TelemetryNameList": ["af39eec9"], + "TypeName": "electric.meter.cac.gt", + "Version": "000", + } + ], + "MultipurposeSensorCacs": [], + "SimpleTempSensorCacs": [ + { + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "MakeModelGtEnumSymbol": "acd93fb3", + "DisplayName": "Adafruit High Temp Waterproof DS18B20 Digital Temp Sensor", + "CommsMethod": "OneWire", + "Exponent": -3, + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TempUnitGtEnumSymbol": "ec14bd47", + "TypicalResponseTimeMs": 880, + "TypeName": "simple.temp.sensor.cac.gt", + "Versoin": "000", + }, + { + "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", + "MakeModelGtEnumSymbol": "00000000", + "TempUnitGtEnumSymbol": "ec14bd47", + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TypicalResponseTimeMs": 0, + "Exponent": -3, + "TypeName": "simple.temp.sensor.cac.gt", + "Version": "000", + }, + { + "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", + "MakeModelGtEnumSymbol": "00000000", + "TempUnitGtEnumSymbol": "ec14bd47", + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TypicalResponseTimeMs": 0, + "Exponent": -3, + "TypeName": "simple.temp.sensor.cac.gt", + "Version": "000", + }, + ], + "OtherCacs": [ + { + "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", + "MakeModelGtEnumSymbol": "00000000", + }, + ], + }) for node in layout.nodes.values(): layout.parent_node(node.alias) From e01fbd80dd530a40514f7312bd9e303516f985de Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 16:09:28 -0400 Subject: [PATCH 033/168] Enable ruff format in pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ef26bca..f72c3cb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,4 +28,4 @@ repos: hooks: - id: ruff args: ["--select", "I"] - # - id: ruff-format + - id: ruff-format From d630cfae07f7a86a73fd4bf47339e083cb2977b9 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 16:13:51 -0400 Subject: [PATCH 034/168] ruff check --fix (generated files excluded) --- src/gwproto/data_classes/hardware_layout.py | 2 +- src/gwproto/decoders.py | 2 +- src/gwproto/property_format.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index c424e8b5..ada2dfa4 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -534,7 +534,7 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: ) telemetry_tuples = [] for node in multi_nodes: - for config in getattr(node.component, "config_list"): + for config in node.component.config_list: telemetry_tuples.append( TelemetryTuple( AboutNode=self.node(config.AboutNodeName), diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index 052c27fd..6822d395 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -289,7 +289,7 @@ def pydantic_named_types( if isinstance(module_names, str): module_names = [module_names] if unimported := [ - module_name for module_name in module_names if not module_name in sys.modules + module_name for module_name in module_names if module_name not in sys.modules ]: raise ValueError(f"ERROR. modules {unimported} have not been imported.") named_types = [] diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 0a650a4c..fada6e29 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -334,7 +334,7 @@ def check_is_uuid_canonical_textual(candidate: str) -> None: except AttributeError as e: raise ValueError(f"Failed to split on -: {e}") if len(x) != 5: - raise ValueError(f"Did not have 5 words") + raise ValueError("Did not have 5 words") for hex_word in x: try: int(hex_word, 16) From cec182649ffe0a922db3b2c7d0c03c903e5da800 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 10 Aug 2024 16:20:51 -0400 Subject: [PATCH 035/168] ruff warnings suppression --- src/gwproto/messages/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gwproto/messages/__init__.py b/src/gwproto/messages/__init__.py index 7054d395..c26d0e5c 100644 --- a/src/gwproto/messages/__init__.py +++ b/src/gwproto/messages/__init__.py @@ -1,3 +1,5 @@ +# ruff: noqa: F405, F403 + from gwproto.gs import * from gwproto.types import * From 967ed73b6306d7cf4ebbd2f1bfe40941ba0c9c23 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:13:12 -0400 Subject: [PATCH 036/168] More careful control of ruff check --- pyproject.toml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ecf23e4c..8dabd2c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,17 +115,24 @@ exclude = [ "dist", ] output-format = "concise" -fix = true +fix = false show-fixes = true -[tool.ruff.format] -preview = true - [tool.ruff.lint] # default select used by ruff: ["E4", "E7", "E9", "F"] -select = ["B","C90","E4", "E7", "E9","F","I","N","PL", "W",] -ignore = ["B027", "PLR0904", "PLW1514", "W191",] -preview = true +# select = ["B","C90","E4", "E7", "E9","F","I","N","PL", "TRY", "W",] +select = ["ALL"] +ignore = [ + "B027", + "COM812", + "D", + "E501", + "EM", + "ISC001", + "PLR0904", + "PLW1514", + "W191", +] [tool.ruff.lint.extend-per-file-ignores] "tests/**/*.py" = [ From c904fd400cf1e24bc8d02b554ca1daa0d8ec91d0 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:13:24 -0400 Subject: [PATCH 037/168] ruff format --- tests/test_misc/test_flush_and_load_house.py | 978 ++++++++++--------- 1 file changed, 490 insertions(+), 488 deletions(-) diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 218161f9..b5136a50 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -94,493 +94,495 @@ def test_flush_and_load_house(): def test_load_real_house(): - layout = HardwareLayout({ - "MyAtomicTNodeGNode": { - "GNodeId": "d636cbeb-c4ad-45cd-b5bc-1c64cc33f4f4", - "Alias": "w.isone.ct.newhaven.orange1", - "DisplayName": "Little Orange House Garage Heating System AtomicTNode", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "AtomicTNode", - }, - "MyTerminalAssetGNode": { - "GNodeId": "137d7f06-ea65-4254-bfd1-5d56fa789229", - "Alias": "w.isone.ct.newhaven.orange1.ta", - "DisplayName": "Little Orange House Garage Heating System TerminalAsset", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "TerminalAsset", - }, - "MyScadaGNode": { - "GNodeId": "28817671-3899-4e24-a337-abcb8633e47a", - "Alias": "w.isone.ct.newhaven.orange1.scada", - "DisplayName": "Little Orange House Garage Heating System SCADA", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "Scada", - }, - "ShNodes": [ - { - "Alias": "a", - "RoleGtEnumSymbol": "6ddff83b", - "ActorClassGtEnumSymbol": "b103058f", - "DisplayName": "AtomicTNode", - "ShNodeId": "7a4fe194-f572-407e-ab65-8d38f83d9eb0", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.home", - "RoleGtEnumSymbol": "863e50d1", - "ActorClassGtEnumSymbol": "32d3d19f", - "DisplayName": "Little Orange House HomeAlone", - "ShNodeId": "a1537de0-5c83-422e-9826-0995e0419953", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.s", - "RoleGtEnumSymbol": "d0afb424", - "ActorClassGtEnumSymbol": "6d37aa41", - "DisplayName": "Little Orange House Main Scada", - "ShNodeId": "19fc828e-0f9f-4a15-819b-6f02e38500c7", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1", - "RoleGtEnumSymbol": "99c5f326", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First 4.5 kW boost in tank", - "ShNodeId": "164d6c73-7c0c-4063-8cf9-01cde3a32b7c", - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "RatedVoltageV": 240, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "30A Relay for first boost element", - "ShNodeId": "a9c94a2f-1800-4394-a90f-4f50dba053ac", - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank", - "RoleGtEnumSymbol": "3ecfe9b8", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Test Axeman Tank", - "ShNodeId": "4ac89701-3472-4fb2-b404-8e3012af0399", - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.m", - "RoleGtEnumSymbol": "9ac68b6e", - "ActorClassGtEnumSymbol": "2ea112b9", - "DisplayName": "Main Power Meter Little Orange House Test System", - "ShNodeId": "55cecbdb-8a47-4160-a1d6-f3617a6279b4", - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe Out of Tank", - "ShNodeId": "1348b9cc-d45e-4095-aa39-24abb58a7498", - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe into Tank", - "ShNodeId": "2aeb26f7-8251-431c-83b5-0ad3c0e4cff0", - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump", - "RoleGtEnumSymbol": "b0eaf2ba", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Circulator Pump", - "ShNodeId": "4dcf1be8-35ea-48c5-a7a0-9d74476b5a8d", - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Circulator Pump Relay", - "ShNodeId": "d6109c64-daec-49b1-8b4f-1c69ab012e69", - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1", - "RoleGtEnumSymbol": "05fdd645", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Single baseboard radiator", - "ShNodeId": "9d8641f7-f5e2-4683-a427-5ebb18345b89", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan", - "RoleGtEnumSymbol": "6896109b", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First baseboard radiator fan", - "ShNodeId": "57b3632f-df3e-4a7f-9205-1e6f953dece9", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Relay for first baseboard radiator fan", - "ShNodeId": "0223a903-ae99-4cd9-bd67-f28e1e799938", - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage", - "RoleGtEnumSymbol": "65725f44", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Garage", - "ShNodeId": "e06809dc-7915-4389-9559-d4a89c3c4994", - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors", - "RoleGtEnumSymbol": "dd975b31", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "43 Avon St Microclimate", - "ShNodeId": "7706f7ae-d01e-4ec2-89f4-8eb6fcc64f18", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.flowmeter1", - "RoleGtEnumSymbol": "ece3b600", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Flow Meter on distribution pipe out of tank", - "ShNodeId": "0cb98277-b4b5-4016-8afa-ed2bffca6750", - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe out of tank", - "ShNodeId": "88b30858-65c0-470b-82e5-0981d3f2b5fe", - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe into tank", - "ShNodeId": "675458cf-2da8-4792-8c50-e04c3f2f8326", - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.temp0", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Tank temp sensor temp0 (on top)", - "ShNodeId": "24e6c994-ff61-43b2-8550-d2017f413cb3", - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage.temp1", - "RoleGtEnumSymbol": "fec74958", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Garage Temp sensor", - "ShNodeId": "12b072ef-0a8b-4569-a055-12b486f35f04", - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors.temp1", - "RoleGtEnumSymbol": "5938bf1f", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Outdoor Temp sensor", - "ShNodeId": "f0821873-e34f-4d53-a69a-10f6bed6d8d8", - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - ], - "ThermalEdges": [ - {"FromNodeAlias": "a.elt1", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank.in", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank", "ToNodeAlias": "a.tank.out"}, - {"FromNodeAlias": "a.tank.out", "ToNodeAlias": "a.tank.out.pump"}, - { - "FromNodeAlias": "a.tank.out.pump", - "ToNodeAlias": "a.tank.out.pump.baseboard1", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.in", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.out.pump.baseboard1.garage", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1.garage", - "ToNodeAlias": "a.outdoors", - }, - ], - "ResistiveHeaterComponents": [ - { - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "DisplayName": "First 4.5 kW boost in tank", - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "TypeName": "resistive.heater.component.gt", - "Version": "000", - "MaxPowerW": 4500, - } - ], - "RelayComponents": [ - { - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "DisplayName": "relay for radiator fan", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 1, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "DisplayName": "relay for first elt in tank", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 2, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "DisplayName": "relay for main circulator pump", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 3, - "NormallyOpen": False, - "TypeName": "relay.component.gt", - "Version": "000", - }, - ], - "MultipurposeSensorComponents": [], - "SimpleTempSensorComponents": [ - { - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "DisplayName": "Outdoor Temperature Sensor", - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "DisplayName": "Temp sensor on pipe out of tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00033ffe", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "DisplayName": "Temp sensor on pipe into tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "000363a9", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "DisplayName": "Component for a.tank.temp0 (on top)", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00041d3f", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "DisplayName": "First garage temp sensor", - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - ], - "ElectricMeterComponents": [ - { - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "DisplayName": "Main power meter for Little orange house garage space heat", - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "HwUid": "1001ab", - "TypeName": "electric.meter.component.gt", - "Version": "000", - } - ], - "PipeFlowSensorComponents": [ - { - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "DisplayName": "Flow meter on pipe out of tank", - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "TypeName": "pipe.flow.sensor.component.gt", - "Version": "000", - } - ], - "OtherComponents": [ - { - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - }, - { - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "DisplayName": "Hydronic pipe out of tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "DisplayName": "Hydronic pipe into tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "DisplayName": "Circulator Pump", - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - }, - { - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "DisplayName": "Little Orange House garage", - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - }, - ], - "ResistiveHeaterCacs": [ - { - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModelGtEnumSymbol": "00000000", - "DisplayName": "Orange Garage heating element", - "NameplateMaxPowerW": 4500, - "RatedVoltageV": 240, - "TypeName": "resistive.heater.cac.gt", - "Version": "000", - } - ], - "RelayCacs": [ - { - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "MakeModelGtEnumSymbol": "fabfa505", - "TypicalResponseTimeMs": 200, - "TypeName": "relay.cac.gt", - "Version": "000", - } - ], - "PipeFlowSensorCacs": [ - { - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModelGtEnumSymbol": "00000000", - "TypeName": "pipe.flow.sensor.cac.gt", - "Version": "000", - } - ], - "ElectricMeterCacs": [ - { - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "MakeModelGtEnumSymbol": "d300635e", - "DisplayName": "Schneider Electric Iem3455 Power Meter", - "InterfaceGtEnumSymbol": "a6a4ac9f", - "PollPeriodMs": 1000, - "DefaultBaud": 9600, - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - ], - "MultipurposeSensorCacs": [], - "SimpleTempSensorCacs": [ - { - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "MakeModelGtEnumSymbol": "acd93fb3", - "DisplayName": "Adafruit High Temp Waterproof DS18B20 Digital Temp Sensor", - "CommsMethod": "OneWire", - "Exponent": -3, - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TempUnitGtEnumSymbol": "ec14bd47", - "TypicalResponseTimeMs": 880, - "TypeName": "simple.temp.sensor.cac.gt", - "Versoin": "000", - }, - { - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - { - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - ], - "OtherCacs": [ - { - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "MakeModelGtEnumSymbol": "00000000", - }, - ], - }) + layout = HardwareLayout( + { + "MyAtomicTNodeGNode": { + "GNodeId": "d636cbeb-c4ad-45cd-b5bc-1c64cc33f4f4", + "Alias": "w.isone.ct.newhaven.orange1", + "DisplayName": "Little Orange House Garage Heating System AtomicTNode", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "AtomicTNode", + }, + "MyTerminalAssetGNode": { + "GNodeId": "137d7f06-ea65-4254-bfd1-5d56fa789229", + "Alias": "w.isone.ct.newhaven.orange1.ta", + "DisplayName": "Little Orange House Garage Heating System TerminalAsset", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "TerminalAsset", + }, + "MyScadaGNode": { + "GNodeId": "28817671-3899-4e24-a337-abcb8633e47a", + "Alias": "w.isone.ct.newhaven.orange1.scada", + "DisplayName": "Little Orange House Garage Heating System SCADA", + "GNodeStatusValue": "Active", + "PrimaryGNodeRoleAlias": "Scada", + }, + "ShNodes": [ + { + "Alias": "a", + "RoleGtEnumSymbol": "6ddff83b", + "ActorClassGtEnumSymbol": "b103058f", + "DisplayName": "AtomicTNode", + "ShNodeId": "7a4fe194-f572-407e-ab65-8d38f83d9eb0", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.home", + "RoleGtEnumSymbol": "863e50d1", + "ActorClassGtEnumSymbol": "32d3d19f", + "DisplayName": "Little Orange House HomeAlone", + "ShNodeId": "a1537de0-5c83-422e-9826-0995e0419953", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.s", + "RoleGtEnumSymbol": "d0afb424", + "ActorClassGtEnumSymbol": "6d37aa41", + "DisplayName": "Little Orange House Main Scada", + "ShNodeId": "19fc828e-0f9f-4a15-819b-6f02e38500c7", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.elt1", + "RoleGtEnumSymbol": "99c5f326", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First 4.5 kW boost in tank", + "ShNodeId": "164d6c73-7c0c-4063-8cf9-01cde3a32b7c", + "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", + "RatedVoltageV": 240, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.elt1.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "30A Relay for first boost element", + "ShNodeId": "a9c94a2f-1800-4394-a90f-4f50dba053ac", + "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank", + "RoleGtEnumSymbol": "3ecfe9b8", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Little Orange House Test Axeman Tank", + "ShNodeId": "4ac89701-3472-4fb2-b404-8e3012af0399", + "ComponentId": "780788df-9706-4299-b116-304a48838338", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.m", + "RoleGtEnumSymbol": "9ac68b6e", + "ActorClassGtEnumSymbol": "2ea112b9", + "DisplayName": "Main Power Meter Little Orange House Test System", + "ShNodeId": "55cecbdb-8a47-4160-a1d6-f3617a6279b4", + "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out", + "RoleGtEnumSymbol": "fe3cbdd5", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Main Heat Distribution Pipe Out of Tank", + "ShNodeId": "1348b9cc-d45e-4095-aa39-24abb58a7498", + "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.in", + "RoleGtEnumSymbol": "fe3cbdd5", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Main Heat Distribution Pipe into Tank", + "ShNodeId": "2aeb26f7-8251-431c-83b5-0ad3c0e4cff0", + "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump", + "RoleGtEnumSymbol": "b0eaf2ba", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Circulator Pump", + "ShNodeId": "4dcf1be8-35ea-48c5-a7a0-9d74476b5a8d", + "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "Circulator Pump Relay", + "ShNodeId": "d6109c64-daec-49b1-8b4f-1c69ab012e69", + "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1", + "RoleGtEnumSymbol": "05fdd645", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Single baseboard radiator", + "ShNodeId": "9d8641f7-f5e2-4683-a427-5ebb18345b89", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.fan", + "RoleGtEnumSymbol": "6896109b", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First baseboard radiator fan", + "ShNodeId": "57b3632f-df3e-4a7f-9205-1e6f953dece9", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.fan.relay", + "RoleGtEnumSymbol": "57b788ee", + "ActorClassGtEnumSymbol": "fddd0064", + "DisplayName": "Relay for first baseboard radiator fan", + "ShNodeId": "0223a903-ae99-4cd9-bd67-f28e1e799938", + "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.garage", + "RoleGtEnumSymbol": "65725f44", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "Little Orange House Garage", + "ShNodeId": "e06809dc-7915-4389-9559-d4a89c3c4994", + "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.outdoors", + "RoleGtEnumSymbol": "dd975b31", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "43 Avon St Microclimate", + "ShNodeId": "7706f7ae-d01e-4ec2-89f4-8eb6fcc64f18", + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.flowmeter1", + "RoleGtEnumSymbol": "ece3b600", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Flow Meter on distribution pipe out of tank", + "ShNodeId": "0cb98277-b4b5-4016-8afa-ed2bffca6750", + "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.temp1", + "RoleGtEnumSymbol": "c480f612", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Temp Sensor on distribution pipe out of tank", + "ShNodeId": "88b30858-65c0-470b-82e5-0981d3f2b5fe", + "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.in.temp1", + "RoleGtEnumSymbol": "c480f612", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Temp Sensor on distribution pipe into tank", + "ShNodeId": "675458cf-2da8-4792-8c50-e04c3f2f8326", + "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.temp0", + "RoleGtEnumSymbol": "73308a1f", + "ActorClassGtEnumSymbol": "dae4b2f0", + "DisplayName": "Tank temp sensor temp0 (on top)", + "ShNodeId": "24e6c994-ff61-43b2-8550-d2017f413cb3", + "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", + "ReportingSamplePeriodS": 5, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.tank.out.pump.baseboard1.garage.temp1", + "RoleGtEnumSymbol": "fec74958", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First Garage Temp sensor", + "ShNodeId": "12b072ef-0a8b-4569-a055-12b486f35f04", + "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", + "ReportingSamplePeriodS": 60, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + { + "Alias": "a.outdoors.temp1", + "RoleGtEnumSymbol": "5938bf1f", + "ActorClassGtEnumSymbol": "00000000", + "DisplayName": "First Outdoor Temp sensor", + "ShNodeId": "f0821873-e34f-4d53-a69a-10f6bed6d8d8", + "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", + "ReportingSamplePeriodS": 60, + "TypeName": "spaceheat.node.gt", + "Version": "100", + }, + ], + "ThermalEdges": [ + {"FromNodeAlias": "a.elt1", "ToNodeAlias": "a.tank"}, + {"FromNodeAlias": "a.tank.in", "ToNodeAlias": "a.tank"}, + {"FromNodeAlias": "a.tank", "ToNodeAlias": "a.tank.out"}, + {"FromNodeAlias": "a.tank.out", "ToNodeAlias": "a.tank.out.pump"}, + { + "FromNodeAlias": "a.tank.out.pump", + "ToNodeAlias": "a.tank.out.pump.baseboard1", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1", + "ToNodeAlias": "a.tank.in", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1", + "ToNodeAlias": "a.tank.out.pump.baseboard1.garage", + }, + { + "FromNodeAlias": "a.tank.out.pump.baseboard1.garage", + "ToNodeAlias": "a.outdoors", + }, + ], + "ResistiveHeaterComponents": [ + { + "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", + "DisplayName": "First 4.5 kW boost in tank", + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "TypeName": "resistive.heater.component.gt", + "Version": "000", + "MaxPowerW": 4500, + } + ], + "RelayComponents": [ + { + "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", + "DisplayName": "relay for radiator fan", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 1, + "NormallyOpen": True, + "TypeName": "relay.component.gt", + "Version": "000", + }, + { + "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", + "DisplayName": "relay for first elt in tank", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 2, + "NormallyOpen": True, + "TypeName": "relay.component.gt", + "Version": "000", + }, + { + "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", + "DisplayName": "relay for main circulator pump", + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "Gpio": 3, + "NormallyOpen": False, + "TypeName": "relay.component.gt", + "Version": "000", + }, + ], + "MultipurposeSensorComponents": [], + "SimpleTempSensorComponents": [ + { + "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", + "DisplayName": "Outdoor Temperature Sensor", + "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", + "DisplayName": "Temp sensor on pipe out of tank", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "00033ffe", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", + "DisplayName": "Temp sensor on pipe into tank", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "000363a9", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", + "DisplayName": "Component for a.tank.temp0 (on top)", + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "HwUid": "00041d3f", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + { + "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", + "DisplayName": "First garage temp sensor", + "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", + "TypeName": "simple.temp.sensor.component.gt", + "Version": "000", + }, + ], + "ElectricMeterComponents": [ + { + "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", + "DisplayName": "Main power meter for Little orange house garage space heat", + "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", + "HwUid": "1001ab", + "TypeName": "electric.meter.component.gt", + "Version": "000", + } + ], + "PipeFlowSensorComponents": [ + { + "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", + "DisplayName": "Flow meter on pipe out of tank", + "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", + "TypeName": "pipe.flow.sensor.component.gt", + "Version": "000", + } + ], + "OtherComponents": [ + { + "ComponentId": "780788df-9706-4299-b116-304a48838338", + "DisplayName": "Little Orange house Axeman Tank", + "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", + }, + { + "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", + "DisplayName": "Hydronic pipe out of tank", + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + }, + { + "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", + "DisplayName": "Hydronic pipe into tank", + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + }, + { + "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", + "DisplayName": "Circulator Pump", + "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", + }, + { + "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", + "DisplayName": "Little Orange House garage", + "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", + }, + ], + "ResistiveHeaterCacs": [ + { + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "MakeModelGtEnumSymbol": "00000000", + "DisplayName": "Orange Garage heating element", + "NameplateMaxPowerW": 4500, + "RatedVoltageV": 240, + "TypeName": "resistive.heater.cac.gt", + "Version": "000", + } + ], + "RelayCacs": [ + { + "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", + "MakeModelGtEnumSymbol": "fabfa505", + "TypicalResponseTimeMs": 200, + "TypeName": "relay.cac.gt", + "Version": "000", + } + ], + "PipeFlowSensorCacs": [ + { + "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", + "MakeModelGtEnumSymbol": "00000000", + "TypeName": "pipe.flow.sensor.cac.gt", + "Version": "000", + } + ], + "ElectricMeterCacs": [ + { + "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", + "MakeModelGtEnumSymbol": "d300635e", + "DisplayName": "Schneider Electric Iem3455 Power Meter", + "InterfaceGtEnumSymbol": "a6a4ac9f", + "PollPeriodMs": 1000, + "DefaultBaud": 9600, + "TelemetryNameList": ["af39eec9"], + "TypeName": "electric.meter.cac.gt", + "Version": "000", + } + ], + "MultipurposeSensorCacs": [], + "SimpleTempSensorCacs": [ + { + "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "MakeModelGtEnumSymbol": "acd93fb3", + "DisplayName": "Adafruit High Temp Waterproof DS18B20 Digital Temp Sensor", + "CommsMethod": "OneWire", + "Exponent": -3, + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TempUnitGtEnumSymbol": "ec14bd47", + "TypicalResponseTimeMs": 880, + "TypeName": "simple.temp.sensor.cac.gt", + "Versoin": "000", + }, + { + "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", + "MakeModelGtEnumSymbol": "00000000", + "TempUnitGtEnumSymbol": "ec14bd47", + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TypicalResponseTimeMs": 0, + "Exponent": -3, + "TypeName": "simple.temp.sensor.cac.gt", + "Version": "000", + }, + { + "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", + "MakeModelGtEnumSymbol": "00000000", + "TempUnitGtEnumSymbol": "ec14bd47", + "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TypicalResponseTimeMs": 0, + "Exponent": -3, + "TypeName": "simple.temp.sensor.cac.gt", + "Version": "000", + }, + ], + "OtherCacs": [ + { + "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", + "MakeModelGtEnumSymbol": "00000000", + }, + { + "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", + "MakeModelGtEnumSymbol": "00000000", + }, + ], + } + ) for node in layout.nodes.values(): layout.parent_node(node.alias) From 7a1046204cc2063d44bbad8842aaf80c6f895be1 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:25:49 -0400 Subject: [PATCH 038/168] ruff warnings - FA not needed; Delete empty generated files for ERA001 --- pyproject.toml | 1 + .../types/test_fibaro_smart_implant_cac_gt.py | 93 --------------- .../test_fibaro_smart_implant_component_gt.py | 111 ------------------ tests/types/test_hubitat_cac_gt.py | 86 -------------- tests/types/test_hubitat_component_gt.py | 111 ------------------ tests/types/test_hubitat_poller_cac_gt.py | 55 --------- .../types/test_hubitat_poller_component_gt.py | 55 --------- tests/types/test_hubitat_tank_cac_gt.py | 86 -------------- tests/types/test_hubitat_tank_component_gt.py | 58 --------- tests/types/test_rest_poller_cac_gt.py | 86 -------------- tests/types/test_rest_poller_component_gt.py | 58 --------- 11 files changed, 1 insertion(+), 799 deletions(-) delete mode 100644 tests/types/test_fibaro_smart_implant_cac_gt.py delete mode 100644 tests/types/test_fibaro_smart_implant_component_gt.py delete mode 100644 tests/types/test_hubitat_cac_gt.py delete mode 100644 tests/types/test_hubitat_component_gt.py delete mode 100644 tests/types/test_hubitat_poller_cac_gt.py delete mode 100644 tests/types/test_hubitat_poller_component_gt.py delete mode 100644 tests/types/test_hubitat_tank_cac_gt.py delete mode 100644 tests/types/test_hubitat_tank_component_gt.py delete mode 100644 tests/types/test_rest_poller_cac_gt.py delete mode 100644 tests/types/test_rest_poller_component_gt.py diff --git a/pyproject.toml b/pyproject.toml index 8dabd2c0..904aba9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,6 +128,7 @@ ignore = [ "D", "E501", "EM", + "FA", # We only support Python >= 3.10, so we shouldn't need this "ISC001", "PLR0904", "PLW1514", diff --git a/tests/types/test_fibaro_smart_implant_cac_gt.py b/tests/types/test_fibaro_smart_implant_cac_gt.py deleted file mode 100644 index c15c1cd5..00000000 --- a/tests/types/test_fibaro_smart_implant_cac_gt.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Tests fibaro.smart.implant.cac.gt type, version 000""" - - -def test_fibaro_smart_implant_cac_gt_generated() -> None: - ... - - # d = { - # "ComponentAttributeClassId": "e676cac9-a77b-46aa-ada3-4d905aeb4f2f", - # "Model": "Fibaro SGS" , - # "DisplayName": "1000 A", - # "TypeName": "fibaro.smart.implant.cac.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # model=gtuple.Model, - # display_name=gtuple.DisplayName, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["Model"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_fibaro_smart_implant_component_gt.py b/tests/types/test_fibaro_smart_implant_component_gt.py deleted file mode 100644 index 8068332d..00000000 --- a/tests/types/test_fibaro_smart_implant_component_gt.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Tests fibaro.smart.implant.component.gt type, version 000""" - - -def test_fibaro_smart_implant_component_gt_generated() -> None: - ... - - # d = { - # "ComponentId": , - # "ComponentAttributeClassId": , - # "ZWaveDSK": , - # "DisplayName": , - # "HwUid": , - # "TypeName": "fibaro.smart.implant.component.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_id=gtuple.ComponentId, - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # z_wave_d_s_k=gtuple.ZWaveDSK, - # display_name=gtuple.DisplayName, - # hw_uid=gtuple.HwUid, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ZWaveDSK"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # if "HwUid" in d2.keys(): - # del d2["HwUid"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_hubitat_cac_gt.py b/tests/types/test_hubitat_cac_gt.py deleted file mode 100644 index b97f468e..00000000 --- a/tests/types/test_hubitat_cac_gt.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests hubitat.cac.gt type, version 000""" - - -def test_hubitat_cac_gt_generated() -> None: - ... - - # d = { - # "ComponentAttributeClassId": , - # "DisplayName": , - # "TypeName": "hubitat.cac.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # display_name=gtuple.DisplayName, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_hubitat_component_gt.py b/tests/types/test_hubitat_component_gt.py deleted file mode 100644 index b508c672..00000000 --- a/tests/types/test_hubitat_component_gt.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Tests hubitat.component.gt type, version 000""" - - -def test_hubitat_component_gt_generated() -> None: - ... - - # d = { - # "ComponentId": , - # "ComponentAttributeClassId": , - # "Hubitat": , - # "DisplayName": , - # "HwUid": , - # "TypeName": "hubitat.component.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_id=gtuple.ComponentId, - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # hubitat=gtuple.Hubitat, - # display_name=gtuple.DisplayName, - # hw_uid=gtuple.HwUid, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["Hubitat"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # if "HwUid" in d2.keys(): - # del d2["HwUid"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_hubitat_poller_cac_gt.py b/tests/types/test_hubitat_poller_cac_gt.py deleted file mode 100644 index af1ca660..00000000 --- a/tests/types/test_hubitat_poller_cac_gt.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests hubitat.poller.cac.gt type, version 000""" - - -def test_hubitat_poller_cac_gt_generated() -> None: - ... - # d = { - # "TypeName": "hubitat.poller.cac.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker().tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) diff --git a/tests/types/test_hubitat_poller_component_gt.py b/tests/types/test_hubitat_poller_component_gt.py deleted file mode 100644 index 1f5771f5..00000000 --- a/tests/types/test_hubitat_poller_component_gt.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests hubitat.poller.component.gt type, version 000""" - - -def test_hubitat_poller_component_gt_generated() -> None: - ... - # d = { - # "TypeName": "hubitat.poller.component.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker().tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) diff --git a/tests/types/test_hubitat_tank_cac_gt.py b/tests/types/test_hubitat_tank_cac_gt.py deleted file mode 100644 index fbcb0976..00000000 --- a/tests/types/test_hubitat_tank_cac_gt.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests hubitat.tank.cac.gt type, version 000""" - - -def test_hubitat_tank_cac_gt_generated() -> None: - ... - - # d = { - # "ComponentAttributeClassId": , - # "DisplayName": , - # "TypeName": "hubitat.tank.cac.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # display_name=gtuple.DisplayName, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_hubitat_tank_component_gt.py b/tests/types/test_hubitat_tank_component_gt.py deleted file mode 100644 index 81bf596d..00000000 --- a/tests/types/test_hubitat_tank_component_gt.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Tests hubitat.tank.component.gt type, version 000""" - - -def test_hubitat_tank_component_gt_generated() -> None: - ... - - # d = { - # "TypeName": "hubitat.tank.component.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) diff --git a/tests/types/test_rest_poller_cac_gt.py b/tests/types/test_rest_poller_cac_gt.py deleted file mode 100644 index 4b59176e..00000000 --- a/tests/types/test_rest_poller_cac_gt.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests rest.poller.cac.gt type, version 000""" - - -def test_rest_poller_cac_gt_generated() -> None: - ... - - # d = { - # "ComponentAttributeClassId": , - # "DisplayName": , - # "TypeName": "rest.poller.cac.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - # component_attribute_class_id=gtuple.ComponentAttributeClassId, - # display_name=gtuple.DisplayName, - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # d2 = dict(d) - # del d2["ComponentAttributeClassId"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Optional attributes can be removed from type - # ###################################### - - # d2 = dict(d) - # if "DisplayName" in d2.keys(): - # del d2["DisplayName"] - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # SchemaError raised if primitive attributes do not have appropriate property_format - # ###################################### - - # d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) - - # # End of Test diff --git a/tests/types/test_rest_poller_component_gt.py b/tests/types/test_rest_poller_component_gt.py deleted file mode 100644 index 5f6e883a..00000000 --- a/tests/types/test_rest_poller_component_gt.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Tests rest.poller.component.gt type, version 000""" - - -def test_rest_poller_component_gt_generated() -> None: - ... - - # d = { - # "TypeName": "rest.poller.component.gt", - # "Version": "000", - # } - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple(d) - - # with pytest.raises(SchemaError): - # Maker.type_to_tuple('"not a dict"') - - # # Test type_to_tuple - # gtype = json.dumps(d) - # gtuple = Maker.type_to_tuple(gtype) - - # # test type_to_tuple and tuple_to_type maps - # assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # # test Maker init - # t = Maker( - - # ).tuple - # assert t == gtuple - - # ###################################### - # # Dataclass related tests - # ###################################### - - # dc = Maker.tuple_to_dc(gtuple) - # assert gtuple == Maker.dc_to_tuple(dc) - # assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - # ###################################### - # # SchemaError raised if missing a required attribute - # ###################################### - - # d2 = dict(d) - # del d2["TypeName"] - # with pytest.raises(SchemaError): - # Maker.dict_to_tuple(d2) - - # ###################################### - # # Behavior on incorrect types - # ###################################### - - # ###################################### - # # SchemaError raised if TypeName is incorrect - # ###################################### - - # d2 = dict(d, TypeName="not the type name") - # with pytest.raises(ValidationError): - # Maker.dict_to_tuple(d2) From 08aa9a6b2d082b730b7120c5a192582baadbd02f Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:27:52 -0400 Subject: [PATCH 039/168] ruff warnings: fix ERA001 --- tests/test_decoders.py | 4 +- tests/test_misc/test_flush_and_load_house.py | 40 -------------------- 2 files changed, 2 insertions(+), 42 deletions(-) diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 238a3095..25eb1471 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -200,7 +200,7 @@ def assert_encode_decode( errors = [] for i, case in enumerate(messages): path_dbg = 0 - # old_len = len(errors) + # old_len = len(errors) # noqa: ERA001 try: decoded = dst_codec.decode( case.src_message.mqtt_topic(), @@ -262,7 +262,7 @@ def assert_encode_decode( print(f"FIRST ERROR, at index {i}") print(f"exp: {case.exp_payload}") print(f"got: {decoded.Payload}") - # print(f"{decoded.message_type():50s}: path:0x{path_dbg:08X} {len(errors) == old_len}") + # print(f"{decoded.message_type():50s}: path:0x{path_dbg:08X} {len(errors) == old_len}") # noqa: ERA001 if errors: raise ValueError(f"ERROR. Got codec matching errors at indices {errors}") diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index b5136a50..7efd13aa 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -1,6 +1,5 @@ """Test load_house module""" -# from actors.config import ScadaSettings from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode from gwproto.types import ElectricMeterCacGt_Maker, SpaceheatNodeGt_Maker @@ -53,45 +52,6 @@ def test_flush_and_load_house(): assert ShNode.by_id["c9456f5b-5a39-4a48-bb91-742a9fdc461d"].alias == "a.m" flush_all() - # layout = HardwareLayout.load(ScadaSettings().paths.hardware_layout) - # assert layout.node("a.m").sh_node_id == "0dd8a803-4724-4f49-b845-14ff57bdb3e6" - # for node in layout.nodes.values(): - # layout.parent_node(node.alias) - # all_nodes = list(layout.nodes.values()) - # assert len(all_nodes) == 26 - # aliases = list(layout.nodes.keys()) - # for i in range(len(aliases)): - # alias = aliases[i] - # assert layout.node(alias) is not None - # nodes_w_components = list( - # filter(lambda x: x.component_id is not None, layout.nodes.values()) - # ) - # assert len(nodes_w_components) == 20 - # actor_nodes_w_components = list(filter(lambda x: x.has_actor, nodes_w_components)) - # assert len(actor_nodes_w_components) == 13 - # tank_water_temp_sensor_nodes = list( - # filter(lambda x: x.role == Role.TankWaterTempSensor, all_nodes) - # ) - # assert len(tank_water_temp_sensor_nodes) == 5 - # for node in tank_water_temp_sensor_nodes: - # assert node.reporting_sample_period_s is not None - # - # flush_all() - # assert RelayComponent.by_id == {} - # assert ElectricMeterComponent.by_id == {} - # assert PipeFlowSensorComponent.by_id == {} - # assert ResistiveHeaterComponent.by_id == {} - # assert SimpleTempSensorComponent.by_id == {} - # assert Component.by_id == {} - # - # assert RelayCac.by_id == {} - # assert ElectricMeterCac.by_id == {} - # assert PipeFlowSensorCac.by_id == {} - # assert ResistiveHeaterCac.by_id == {} - # assert SimpleTempSensorCac.by_id == {} - # assert ComponentAttributeClass.by_id == {} - # assert ShNode.by_id == {} - def test_load_real_house(): layout = HardwareLayout( From 761eda688544bfa780f4ac18ce5b969c3a127e1e Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:34:06 -0400 Subject: [PATCH 040/168] ruff check --fix --select SIM118 --- src/gwproto/enums/actor_class.py | 6 ++--- src/gwproto/enums/local_comm_interface.py | 6 ++--- src/gwproto/enums/make_model.py | 6 ++--- src/gwproto/enums/role.py | 6 ++--- src/gwproto/enums/telemetry_name.py | 6 ++--- src/gwproto/enums/unit.py | 6 ++--- .../types/component_attribute_class_gt.py | 6 ++--- src/gwproto/types/component_gt.py | 8 +++---- src/gwproto/types/data_channel.py | 12 +++++----- src/gwproto/types/egauge_io.py | 8 +++---- src/gwproto/types/egauge_register_config.py | 16 +++++++------- src/gwproto/types/electric_meter_cac_gt.py | 14 ++++++------ .../types/electric_meter_component_gt.py | 12 +++++----- src/gwproto/types/gt_dispatch_boolean.py | 16 +++++++------- .../types/gt_dispatch_boolean_local.py | 12 +++++----- .../types/gt_driver_booleanactuator_cmd.py | 10 ++++----- .../types/gt_sh_booleanactuator_cmd_status.py | 10 ++++----- src/gwproto/types/gt_sh_cli_atn_cmd.py | 10 ++++----- .../gt_sh_multipurpose_telemetry_status.py | 14 ++++++------ .../types/gt_sh_simple_telemetry_status.py | 12 +++++----- src/gwproto/types/gt_sh_status.py | 22 +++++++++---------- ...t_sh_telemetry_from_multipurpose_sensor.py | 12 +++++----- src/gwproto/types/gt_telemetry.py | 12 +++++----- src/gwproto/types/heartbeat_b.py | 18 +++++++-------- .../types/multipurpose_sensor_cac_gt.py | 16 +++++++------- .../types/multipurpose_sensor_component_gt.py | 12 +++++----- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 8 +++---- .../types/pipe_flow_sensor_component_gt.py | 12 +++++----- src/gwproto/types/power_watts.py | 6 ++--- src/gwproto/types/relay_cac_gt.py | 10 ++++----- src/gwproto/types/relay_component_gt.py | 10 ++++----- src/gwproto/types/resistive_heater_cac_gt.py | 12 +++++----- .../types/resistive_heater_component_gt.py | 8 +++---- .../types/simple_temp_sensor_cac_gt.py | 16 +++++++------- .../types/simple_temp_sensor_component_gt.py | 8 +++---- src/gwproto/types/snapshot_spaceheat.py | 10 ++++----- src/gwproto/types/spaceheat_node_gt.py | 12 +++++----- src/gwproto/types/ta_data_channels.py | 16 +++++++------- .../types/telemetry_reporting_config.py | 16 +++++++------- .../types/telemetry_snapshot_spaceheat.py | 12 +++++----- 40 files changed, 222 insertions(+), 222 deletions(-) diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 2616590f..418c296d 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -107,7 +107,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -138,7 +138,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "NoActor". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -156,7 +156,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 9c30af48..78041f1a 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -70,7 +70,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -101,7 +101,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "Unknown". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -119,7 +119,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index fe0d7748..5d9709d6 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -115,7 +115,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -146,7 +146,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "UnknownMake__UnknownModel". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -164,7 +164,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 15b73752..39d87299 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -103,7 +103,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -134,7 +134,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "Unknown". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -152,7 +152,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index d446ea96..4bbe7fab 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -92,7 +92,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -123,7 +123,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "Unknown". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -141,7 +141,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 66b7a02b..48603c0a 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -74,7 +74,7 @@ def version(cls, value: str) -> str: """ if not isinstance(value, str): raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -105,7 +105,7 @@ def symbol_to_value(cls, symbol: str) -> str: recognized - which could happen if the actor making the symbol is using a later version of this enum, returns the default value of "Unknown". """ - if symbol not in symbol_to_value.keys(): + if symbol not in symbol_to_value: return cls.default().value return symbol_to_value[symbol] @@ -123,7 +123,7 @@ def value_to_symbol(cls, value: str) -> str: of this enum than the actor decoding the message, returns the default symbol of "00000000". """ - if value not in value_to_symbol.keys(): + if value not in value_to_symbol: return value_to_symbol[cls.default().value] return value_to_symbol[value] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index ec500f6c..b14939ab 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -171,11 +171,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentAttributeClassGt: ComponentAttributeClassGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index e466df2d..d47b7391 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -197,13 +197,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentGt: ComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py index 8675777c..98ea8de6 100644 --- a/src/gwproto/types/data_channel.py +++ b/src/gwproto/types/data_channel.py @@ -188,19 +188,19 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> DataChannel: DataChannel """ d2 = dict(d) - if "DisplayName" not in d2.keys(): + if "DisplayName" not in d2: raise SchemaError(f"dict missing DisplayName: <{d2}>") - if "AboutName" not in d2.keys(): + if "AboutName" not in d2: raise SchemaError(f"dict missing AboutName: <{d2}>") - if "CapturedByName" not in d2.keys(): + if "CapturedByName" not in d2: raise SchemaError(f"dict missing CapturedByName: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2.keys(): + if "TelemetryNameGtEnumSymbol" not in d2: raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) d2["TelemetryName"] = EnumTelemetryName(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index d1f12960..b5cbe287 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -166,7 +166,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeIo: EgaugeIo """ d2 = dict(d) - if "InputConfig" not in d2.keys(): + if "InputConfig" not in d2: raise SchemaError(f"dict missing InputConfig: <{d2}>") if not isinstance(d2["InputConfig"], dict): raise SchemaError( @@ -174,7 +174,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeIo: ) input_config = EgaugeRegisterConfig_Maker.dict_to_tuple(d2["InputConfig"]) d2["InputConfig"] = input_config - if "OutputConfig" not in d2.keys(): + if "OutputConfig" not in d2: raise SchemaError(f"dict missing OutputConfig: <{d2}>") if not isinstance(d2["OutputConfig"], dict): raise SchemaError( @@ -182,9 +182,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeIo: ) output_config = TelemetryReportingConfig_Maker.dict_to_tuple(d2["OutputConfig"]) d2["OutputConfig"] = output_config - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index 812b0961..c7aabcc9 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -185,21 +185,21 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeRegisterConfig: EgaugeRegisterConfig """ d2 = dict(d) - if "Address" not in d2.keys(): + if "Address" not in d2: raise SchemaError(f"dict missing Address: <{d2}>") - if "Name" not in d2.keys(): + if "Name" not in d2: raise SchemaError(f"dict missing Name: <{d2}>") - if "Description" not in d2.keys(): + if "Description" not in d2: raise SchemaError(f"dict missing Description: <{d2}>") - if "Type" not in d2.keys(): + if "Type" not in d2: raise SchemaError(f"dict missing Type: <{d2}>") - if "Denominator" not in d2.keys(): + if "Denominator" not in d2: raise SchemaError(f"dict missing Denominator: <{d2}>") - if "Unit" not in d2.keys(): + if "Unit" not in d2: raise SchemaError(f"dict missing Unit: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 7bb12950..a2e409ad 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -210,13 +210,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: ElectricMeterCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "TelemetryNameList" not in d2.keys(): + if "TelemetryNameList" not in d2: raise SchemaError(f"dict <{d2}> missing TelemetryNameList") if not isinstance(d2["TelemetryNameList"], List): raise SchemaError("TelemetryNameList must be a List!") @@ -225,15 +225,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: value = TelemetryName.symbol_to_value(elt) telemetry_name_list.append(TelemetryName(value)) d2["TelemetryNameList"] = telemetry_name_list - if "PollPeriodMs" not in d2.keys(): + if "PollPeriodMs" not in d2: raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") - if "InterfaceGtEnumSymbol" not in d2.keys(): + if "InterfaceGtEnumSymbol" not in d2: raise SchemaError(f"InterfaceGtEnumSymbol missing from dict <{d2}>") value = LocalCommInterface.symbol_to_value(d2["InterfaceGtEnumSymbol"]) d2["Interface"] = LocalCommInterface(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index af9bc29e..03e433be 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -300,11 +300,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: ElectricMeterComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "ConfigList" not in d2.keys(): + if "ConfigList" not in d2: raise SchemaError(f"dict missing ConfigList: <{d2}>") if not isinstance(d2["ConfigList"], List): raise SchemaError(f"ConfigList <{d2['ConfigList']}> must be a List!") @@ -317,7 +317,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: t = TelemetryReportingConfig_Maker.dict_to_tuple(elt) config_list.append(t) d2["ConfigList"] = config_list - if "EgaugeIoList" not in d2.keys(): + if "EgaugeIoList" not in d2: raise SchemaError(f"dict missing EgaugeIoList: <{d2}>") if not isinstance(d2["EgaugeIoList"], List): raise SchemaError(f"EgaugeIoList <{d2['EgaugeIoList']}> must be a List!") @@ -330,9 +330,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: t = EgaugeIo_Maker.dict_to_tuple(elt) egauge_io_list.append(t) d2["EgaugeIoList"] = egauge_io_list - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index df1bf989..f84aa9e0 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -228,21 +228,21 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtDispatchBoolean: GtDispatchBoolean """ d2 = dict(d) - if "AboutNodeName" not in d2.keys(): + if "AboutNodeName" not in d2: raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "ToGNodeAlias" not in d2.keys(): + if "ToGNodeAlias" not in d2: raise SchemaError(f"dict missing ToGNodeAlias: <{d2}>") - if "FromGNodeAlias" not in d2.keys(): + if "FromGNodeAlias" not in d2: raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2.keys(): + if "FromGNodeInstanceId" not in d2: raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "RelayState" not in d2.keys(): + if "RelayState" not in d2: raise SchemaError(f"dict missing RelayState: <{d2}>") - if "SendTimeUnixMs" not in d2.keys(): + if "SendTimeUnixMs" not in d2: raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "110": LOGGER.debug( diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index b99ee874..1563b29d 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -199,17 +199,17 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtDispatchBooleanLocal: GtDispatchBooleanLocal """ d2 = dict(d) - if "RelayState" not in d2.keys(): + if "RelayState" not in d2: raise SchemaError(f"dict missing RelayState: <{d2}>") - if "AboutNodeName" not in d2.keys(): + if "AboutNodeName" not in d2: raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "FromNodeName" not in d2.keys(): + if "FromNodeName" not in d2: raise SchemaError(f"dict missing FromNodeName: <{d2}>") - if "SendTimeUnixMs" not in d2.keys(): + if "SendTimeUnixMs" not in d2: raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "110": LOGGER.debug( diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 6affb272..95a05895 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -177,15 +177,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtDriverBooleanactuatorCmd: GtDriverBooleanactuatorCmd """ d2 = dict(d) - if "RelayState" not in d2.keys(): + if "RelayState" not in d2: raise SchemaError(f"dict missing RelayState: <{d2}>") - if "ShNodeAlias" not in d2.keys(): + if "ShNodeAlias" not in d2: raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "CommandTimeUnixMs" not in d2.keys(): + if "CommandTimeUnixMs" not in d2: raise SchemaError(f"dict missing CommandTimeUnixMs: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 957172af..4b367031 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -178,15 +178,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShBooleanactuatorCmdStatus: GtShBooleanactuatorCmdStatus """ d2 = dict(d) - if "ShNodeAlias" not in d2.keys(): + if "ShNodeAlias" not in d2: raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "RelayStateCommandList" not in d2.keys(): + if "RelayStateCommandList" not in d2: raise SchemaError(f"dict missing RelayStateCommandList: <{d2}>") - if "CommandTimeUnixMsList" not in d2.keys(): + if "CommandTimeUnixMsList" not in d2: raise SchemaError(f"dict missing CommandTimeUnixMsList: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index 86f21132..1d879634 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -175,15 +175,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShCliAtnCmd: GtShCliAtnCmd """ d2 = dict(d) - if "FromGNodeAlias" not in d2.keys(): + if "FromGNodeAlias" not in d2: raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "SendSnapshot" not in d2.keys(): + if "SendSnapshot" not in d2: raise SchemaError(f"dict missing SendSnapshot: <{d2}>") - if "FromGNodeId" not in d2.keys(): + if "FromGNodeId" not in d2: raise SchemaError(f"dict missing FromGNodeId: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "110": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 3ca4b1ea..f693b81c 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -221,21 +221,21 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShMultipurposeTelemetryStatus: GtShMultipurposeTelemetryStatus """ d2 = dict(d) - if "AboutNodeAlias" not in d2.keys(): + if "AboutNodeAlias" not in d2: raise SchemaError(f"dict missing AboutNodeAlias: <{d2}>") - if "SensorNodeAlias" not in d2.keys(): + if "SensorNodeAlias" not in d2: raise SchemaError(f"dict missing SensorNodeAlias: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2.keys(): + if "TelemetryNameGtEnumSymbol" not in d2: raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) d2["TelemetryName"] = EnumTelemetryName(value) - if "ValueList" not in d2.keys(): + if "ValueList" not in d2: raise SchemaError(f"dict missing ValueList: <{d2}>") - if "ReadTimeUnixMsList" not in d2.keys(): + if "ReadTimeUnixMsList" not in d2: raise SchemaError(f"dict missing ReadTimeUnixMsList: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index c41f3bb2..289a3510 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -206,19 +206,19 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShSimpleTelemetryStatus: GtShSimpleTelemetryStatus """ d2 = dict(d) - if "ShNodeAlias" not in d2.keys(): + if "ShNodeAlias" not in d2: raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2.keys(): + if "TelemetryNameGtEnumSymbol" not in d2: raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) d2["TelemetryName"] = EnumTelemetryName(value) - if "ValueList" not in d2.keys(): + if "ValueList" not in d2: raise SchemaError(f"dict missing ValueList: <{d2}>") - if "ReadTimeUnixMsList" not in d2.keys(): + if "ReadTimeUnixMsList" not in d2: raise SchemaError(f"dict missing ReadTimeUnixMsList: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 714360dd..bda93ce6 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -253,17 +253,17 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: GtShStatus """ d2 = dict(d) - if "FromGNodeAlias" not in d2.keys(): + if "FromGNodeAlias" not in d2: raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeId" not in d2.keys(): + if "FromGNodeId" not in d2: raise SchemaError(f"dict missing FromGNodeId: <{d2}>") - if "AboutGNodeAlias" not in d2.keys(): + if "AboutGNodeAlias" not in d2: raise SchemaError(f"dict missing AboutGNodeAlias: <{d2}>") - if "SlotStartUnixS" not in d2.keys(): + if "SlotStartUnixS" not in d2: raise SchemaError(f"dict missing SlotStartUnixS: <{d2}>") - if "ReportingPeriodS" not in d2.keys(): + if "ReportingPeriodS" not in d2: raise SchemaError(f"dict missing ReportingPeriodS: <{d2}>") - if "SimpleTelemetryList" not in d2.keys(): + if "SimpleTelemetryList" not in d2: raise SchemaError(f"dict missing SimpleTelemetryList: <{d2}>") if not isinstance(d2["SimpleTelemetryList"], List): raise SchemaError( @@ -278,7 +278,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: t = GtShSimpleTelemetryStatus_Maker.dict_to_tuple(elt) simple_telemetry_list.append(t) d2["SimpleTelemetryList"] = simple_telemetry_list - if "MultipurposeTelemetryList" not in d2.keys(): + if "MultipurposeTelemetryList" not in d2: raise SchemaError(f"dict missing MultipurposeTelemetryList: <{d2}>") if not isinstance(d2["MultipurposeTelemetryList"], List): raise SchemaError( @@ -293,7 +293,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: t = GtShMultipurposeTelemetryStatus_Maker.dict_to_tuple(elt) multipurpose_telemetry_list.append(t) d2["MultipurposeTelemetryList"] = multipurpose_telemetry_list - if "BooleanactuatorCmdList" not in d2.keys(): + if "BooleanactuatorCmdList" not in d2: raise SchemaError(f"dict missing BooleanactuatorCmdList: <{d2}>") if not isinstance(d2["BooleanactuatorCmdList"], List): raise SchemaError( @@ -308,11 +308,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: t = GtShBooleanactuatorCmdStatus_Maker.dict_to_tuple(elt) booleanactuator_cmd_list.append(t) d2["BooleanactuatorCmdList"] = booleanactuator_cmd_list - if "StatusUid" not in d2.keys(): + if "StatusUid" not in d2: raise SchemaError(f"dict missing StatusUid: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "110": LOGGER.debug( diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 657268d3..df06115d 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -207,11 +207,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShTelemetryFromMultipurposeSensor GtShTelemetryFromMultipurposeSensor """ d2 = dict(d) - if "ScadaReadTimeUnixMs" not in d2.keys(): + if "ScadaReadTimeUnixMs" not in d2: raise SchemaError(f"dict missing ScadaReadTimeUnixMs: <{d2}>") - if "AboutNodeAliasList" not in d2.keys(): + if "AboutNodeAliasList" not in d2: raise SchemaError(f"dict missing AboutNodeAliasList: <{d2}>") - if "TelemetryNameList" not in d2.keys(): + if "TelemetryNameList" not in d2: raise SchemaError(f"dict <{d2}> missing TelemetryNameList") if not isinstance(d2["TelemetryNameList"], List): raise SchemaError("TelemetryNameList must be a List!") @@ -220,11 +220,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtShTelemetryFromMultipurposeSensor value = TelemetryName.symbol_to_value(elt) telemetry_name_list.append(TelemetryName(value)) d2["TelemetryNameList"] = telemetry_name_list - if "ValueList" not in d2.keys(): + if "ValueList" not in d2: raise SchemaError(f"dict missing ValueList: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 3cc187b1..52957a34 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -180,19 +180,19 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> GtTelemetry: GtTelemetry """ d2 = dict(d) - if "ScadaReadTimeUnixMs" not in d2.keys(): + if "ScadaReadTimeUnixMs" not in d2: raise SchemaError(f"dict missing ScadaReadTimeUnixMs: <{d2}>") - if "Value" not in d2.keys(): + if "Value" not in d2: raise SchemaError(f"dict missing Value: <{d2}>") - if "NameGtEnumSymbol" not in d2.keys(): + if "NameGtEnumSymbol" not in d2: raise SchemaError(f"NameGtEnumSymbol missing from dict <{d2}>") value = TelemetryName.symbol_to_value(d2["NameGtEnumSymbol"]) d2["Name"] = TelemetryName(value) - if "Exponent" not in d2.keys(): + if "Exponent" not in d2: raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "110": LOGGER.debug( diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index ab097e7b..f649b5ad 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -233,23 +233,23 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: HeartbeatB """ d2 = dict(d) - if "FromGNodeAlias" not in d2.keys(): + if "FromGNodeAlias" not in d2: raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2.keys(): + if "FromGNodeInstanceId" not in d2: raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "MyHex" not in d2.keys(): + if "MyHex" not in d2: raise SchemaError(f"dict missing MyHex: <{d2}>") - if "YourLastHex" not in d2.keys(): + if "YourLastHex" not in d2: raise SchemaError(f"dict missing YourLastHex: <{d2}>") - if "LastReceivedTimeUnixMs" not in d2.keys(): + if "LastReceivedTimeUnixMs" not in d2: raise SchemaError(f"dict missing LastReceivedTimeUnixMs: <{d2}>") - if "SendTimeUnixMs" not in d2.keys(): + if "SendTimeUnixMs" not in d2: raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "StartingOver" not in d2.keys(): + if "StartingOver" not in d2: raise SchemaError(f"dict missing StartingOver: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "001": LOGGER.debug( diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 077f097d..11e23e47 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -227,21 +227,21 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: MultipurposeSensorCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "PollPeriodMs" not in d2.keys(): + if "PollPeriodMs" not in d2: raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") - if "Exponent" not in d2.keys(): + if "Exponent" not in d2: raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TempUnitGtEnumSymbol" not in d2.keys(): + if "TempUnitGtEnumSymbol" not in d2: raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) d2["TempUnit"] = Unit(value) - if "TelemetryNameList" not in d2.keys(): + if "TelemetryNameList" not in d2: raise SchemaError(f"dict <{d2}> missing TelemetryNameList") if not isinstance(d2["TelemetryNameList"], List): raise SchemaError("TelemetryNameList must be a List!") @@ -250,9 +250,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: value = TelemetryName.symbol_to_value(elt) telemetry_name_list.append(TelemetryName(value)) d2["TelemetryNameList"] = telemetry_name_list - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 36963556..add78964 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -218,13 +218,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: MultipurposeSensorComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "ChannelList" not in d2.keys(): + if "ChannelList" not in d2: raise SchemaError(f"dict missing ChannelList: <{d2}>") - if "ConfigList" not in d2.keys(): + if "ConfigList" not in d2: raise SchemaError(f"dict missing ConfigList: <{d2}>") if not isinstance(d2["ConfigList"], List): raise SchemaError(f"ConfigList <{d2['ConfigList']}> must be a List!") @@ -237,9 +237,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: t = TelemetryReportingConfig_Maker.dict_to_tuple(elt) config_list.append(t) d2["ConfigList"] = config_list - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index cee374b2..2ae0610d 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -177,15 +177,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorCacGt: PipeFlowSensorCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 423fc54c..ac707e03 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -213,17 +213,17 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorComponentGt: PipeFlowSensorComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "I2cAddress" not in d2.keys(): + if "I2cAddress" not in d2: raise SchemaError(f"dict missing I2cAddress: <{d2}>") - if "ConversionFactor" not in d2.keys(): + if "ConversionFactor" not in d2: raise SchemaError(f"dict missing ConversionFactor: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 0ddd2810..3c1f39dc 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -142,11 +142,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> PowerWatts: PowerWatts """ d2 = dict(d) - if "Watts" not in d2.keys(): + if "Watts" not in d2: raise SchemaError(f"dict missing Watts: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 18963091..5bfc2906 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -175,17 +175,17 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> RelayCacGt: RelayCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "TypicalResponseTimeMs" not in d2.keys(): + if "TypicalResponseTimeMs" not in d2: raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 62d868eb..8130b6c8 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -211,15 +211,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> RelayComponentGt: RelayComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "NormallyOpen" not in d2.keys(): + if "NormallyOpen" not in d2: raise SchemaError(f"dict missing NormallyOpen: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 0223f26b..1cac5011 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -190,19 +190,19 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterCacGt: ResistiveHeaterCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "NameplateMaxPowerW" not in d2.keys(): + if "NameplateMaxPowerW" not in d2: raise SchemaError(f"dict missing NameplateMaxPowerW: <{d2}>") - if "RatedVoltageV" not in d2.keys(): + if "RatedVoltageV" not in d2: raise SchemaError(f"dict missing RatedVoltageV: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 7bd713a5..525acc3c 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -208,13 +208,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterComponentGt: ResistiveHeaterComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 032d8d69..76fb8f70 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -209,27 +209,27 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorCacGt: SimpleTempSensorCacGt """ d2 = dict(d) - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2.keys(): + if "MakeModelGtEnumSymbol" not in d2: raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) d2["MakeModel"] = EnumMakeModel(value) - if "TypicalResponseTimeMs" not in d2.keys(): + if "TypicalResponseTimeMs" not in d2: raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") - if "Exponent" not in d2.keys(): + if "Exponent" not in d2: raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TempUnitGtEnumSymbol" not in d2.keys(): + if "TempUnitGtEnumSymbol" not in d2: raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) d2["TempUnit"] = Unit(value) - if "TelemetryNameGtEnumSymbol" not in d2.keys(): + if "TelemetryNameGtEnumSymbol" not in d2: raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) d2["TelemetryName"] = EnumTelemetryName(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 85ffd3b1..dbce2e8d 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -204,13 +204,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorComponentGt: SimpleTempSensorComponentGt """ d2 = dict(d) - if "ComponentId" not in d2.keys(): + if "ComponentId" not in d2: raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2.keys(): + if "ComponentAttributeClassId" not in d2: raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 241819dc..a13ac7ed 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -169,11 +169,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SnapshotSpaceheat: SnapshotSpaceheat """ d2 = dict(d) - if "FromGNodeAlias" not in d2.keys(): + if "FromGNodeAlias" not in d2: raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2.keys(): + if "FromGNodeInstanceId" not in d2: raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "Snapshot" not in d2.keys(): + if "Snapshot" not in d2: raise SchemaError(f"dict missing Snapshot: <{d2}>") if not isinstance(d2["Snapshot"], dict): raise SchemaError( @@ -181,9 +181,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SnapshotSpaceheat: ) snapshot = TelemetrySnapshotSpaceheat_Maker.dict_to_tuple(d2["Snapshot"]) d2["Snapshot"] = snapshot - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index b75f3362..2153e39c 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -257,21 +257,21 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SpaceheatNodeGt: SpaceheatNodeGt """ d2 = dict(d) - if "ShNodeId" not in d2.keys(): + if "ShNodeId" not in d2: raise SchemaError(f"dict missing ShNodeId: <{d2}>") - if "Alias" not in d2.keys(): + if "Alias" not in d2: raise SchemaError(f"dict missing Alias: <{d2}>") - if "ActorClassGtEnumSymbol" not in d2.keys(): + if "ActorClassGtEnumSymbol" not in d2: raise SchemaError(f"ActorClassGtEnumSymbol missing from dict <{d2}>") value = EnumActorClass.symbol_to_value(d2["ActorClassGtEnumSymbol"]) d2["ActorClass"] = EnumActorClass(value) - if "RoleGtEnumSymbol" not in d2.keys(): + if "RoleGtEnumSymbol" not in d2: raise SchemaError(f"RoleGtEnumSymbol missing from dict <{d2}>") value = EnumRole.symbol_to_value(d2["RoleGtEnumSymbol"]) d2["Role"] = EnumRole(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "100": LOGGER.debug( diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index c74a96b7..9b0c80f4 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -219,15 +219,15 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> TaDataChannels: TaDataChannels """ d2 = dict(d) - if "TerminalAssetGNodeAlias" not in d2.keys(): + if "TerminalAssetGNodeAlias" not in d2: raise SchemaError(f"dict missing TerminalAssetGNodeAlias: <{d2}>") - if "TerminalAssetGNodeId" not in d2.keys(): + if "TerminalAssetGNodeId" not in d2: raise SchemaError(f"dict missing TerminalAssetGNodeId: <{d2}>") - if "TimeUnixS" not in d2.keys(): + if "TimeUnixS" not in d2: raise SchemaError(f"dict missing TimeUnixS: <{d2}>") - if "Author" not in d2.keys(): + if "Author" not in d2: raise SchemaError(f"dict missing Author: <{d2}>") - if "Channels" not in d2.keys(): + if "Channels" not in d2: raise SchemaError(f"dict missing Channels: <{d2}>") if not isinstance(d2["Channels"], List): raise SchemaError(f"Channels <{d2['Channels']}> must be a List!") @@ -240,11 +240,11 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> TaDataChannels: t = DataChannel_Maker.dict_to_tuple(elt) channels.append(t) d2["Channels"] = channels - if "Identifier" not in d2.keys(): + if "Identifier" not in d2: raise SchemaError(f"dict missing Identifier: <{d2}>") - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 74b28dce..4b5fad76 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -222,25 +222,25 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> TelemetryReportingConfig: TelemetryReportingConfig """ d2 = dict(d) - if "TelemetryNameGtEnumSymbol" not in d2.keys(): + if "TelemetryNameGtEnumSymbol" not in d2: raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) d2["TelemetryName"] = EnumTelemetryName(value) - if "AboutNodeName" not in d2.keys(): + if "AboutNodeName" not in d2: raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "ReportOnChange" not in d2.keys(): + if "ReportOnChange" not in d2: raise SchemaError(f"dict missing ReportOnChange: <{d2}>") - if "SamplePeriodS" not in d2.keys(): + if "SamplePeriodS" not in d2: raise SchemaError(f"dict missing SamplePeriodS: <{d2}>") - if "Exponent" not in d2.keys(): + if "Exponent" not in d2: raise SchemaError(f"dict missing Exponent: <{d2}>") - if "UnitGtEnumSymbol" not in d2.keys(): + if "UnitGtEnumSymbol" not in d2: raise SchemaError(f"UnitGtEnumSymbol missing from dict <{d2}>") value = EnumUnit.symbol_to_value(d2["UnitGtEnumSymbol"]) d2["Unit"] = EnumUnit(value) - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 1af6ae4b..959e6a0e 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -208,13 +208,13 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> TelemetrySnapshotSpaceheat: TelemetrySnapshotSpaceheat """ d2 = dict(d) - if "ReportTimeUnixMs" not in d2.keys(): + if "ReportTimeUnixMs" not in d2: raise SchemaError(f"dict missing ReportTimeUnixMs: <{d2}>") - if "AboutNodeAliasList" not in d2.keys(): + if "AboutNodeAliasList" not in d2: raise SchemaError(f"dict missing AboutNodeAliasList: <{d2}>") - if "ValueList" not in d2.keys(): + if "ValueList" not in d2: raise SchemaError(f"dict missing ValueList: <{d2}>") - if "TelemetryNameList" not in d2.keys(): + if "TelemetryNameList" not in d2: raise SchemaError(f"dict <{d2}> missing TelemetryNameList") if not isinstance(d2["TelemetryNameList"], List): raise SchemaError("TelemetryNameList must be a List!") @@ -223,9 +223,9 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> TelemetrySnapshotSpaceheat: value = TelemetryName.symbol_to_value(elt) telemetry_name_list.append(TelemetryName(value)) d2["TelemetryNameList"] = telemetry_name_list - if "TypeName" not in d2.keys(): + if "TypeName" not in d2: raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): + if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") if d2["Version"] != "000": LOGGER.debug( From e5eec43c2067d5e40e288e454884397ca2dbee3d Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:41:00 -0400 Subject: [PATCH 041/168] ruff check --fix --select SIM910 --- src/gwproto/message.py | 4 ++-- src/gwproto/types/electric_meter_component_gt.py | 10 +++++----- .../types/gt_sh_multipurpose_telemetry_status.py | 4 ++-- src/gwproto/types/gt_sh_simple_telemetry_status.py | 4 ++-- .../types/gt_sh_telemetry_from_multipurpose_sensor.py | 6 +++--- src/gwproto/types/hubitat_component_gt.py | 2 +- src/gwproto/types/telemetry_reporting_config.py | 4 ++-- src/gwproto/types/telemetry_snapshot_spaceheat.py | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/gwproto/message.py b/src/gwproto/message.py index 5514c299..60b8458c 100644 --- a/src/gwproto/message.py +++ b/src/gwproto/message.py @@ -35,7 +35,7 @@ class Header(BaseModel): def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict) -> None: if arg_name not in kwargs_dict: - payload = kwargs_dict.get("Payload", None) + payload = kwargs_dict.get("Payload") if payload is None or not hasattr(payload, arg_name): kwargs_dict[arg_name] = default_value @@ -75,7 +75,7 @@ def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> Header: ("MessageType", PAYLOAD_TYPE_FIELDS), ("AckRequired", ["AckRequired"]), ]: - val = kwargs.get(header_field, None) + val = kwargs.get(header_field) if val is None: for payload_field in payload_fields: if hasattr(payload, payload_field): diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 03e433be..585b63aa 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -132,8 +132,8 @@ def check_axiom_1(cls, v: dict) -> dict: ModbusHost is None if and only if ModbusPort is None """ # TODO: Implement check for axiom 1" - ModbusHost = v.get("ModbusHost", None) - ModbusPort = v.get("ModbusHost", None) + ModbusHost = v.get("ModbusHost") + ModbusPort = v.get("ModbusHost") if ModbusHost is None and not (ModbusPort is None): raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") if not (ModbusHost is None) and ModbusPort is None: @@ -148,9 +148,9 @@ def check_axiom_2(cls, v: dict) -> dict: the set of output configs is equal to ConfigList as a set """ # TODO: Implement check for axiom 2" - EgaugeIoList = v.get("EgaugeIoList", None) - ModbusHost = v.get("ModbusHost", None) - ConfigList = v.get("ConfigList", None) + EgaugeIoList = v.get("EgaugeIoList") + ModbusHost = v.get("ModbusHost") + ConfigList = v.get("ConfigList") if len(EgaugeIoList) == 0: return v diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index f693b81c..f15a5174 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -91,8 +91,8 @@ def check_axiom_1(cls, v: dict) -> dict: Axiom 1: ListLengthConsistency. ValueList and ReadTimeUnixMsList must have the same length. """ - value_list: List[int] = v.get("ValueList", None) - time_list: List[int] = v.get("ReadTimeUnixMsList", None) + value_list: List[int] = v.get("ValueList") + time_list: List[int] = v.get("ReadTimeUnixMsList") if len(value_list) != len(time_list): raise ValueError( "Axiom 1: ValueList and ReadTimeUnixMsList must have the same length." diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 289a3510..138d9e04 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -78,8 +78,8 @@ def check_axiom_1(cls, v: dict) -> dict: Axiom 1: ListLengthConsistency. ValueList and ReadTimeUnixMsList must have the same length. """ - value_list: List[int] = v.get("ValueList", None) - time_list: List[int] = v.get("ReadTimeUnixMsList", None) + value_list: List[int] = v.get("ValueList") + time_list: List[int] = v.get("ReadTimeUnixMsList") if len(value_list) != len(time_list): raise ValueError( "Axiom 1: ValueList and ReadTimeUnixMsList must have the same length." diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index df06115d..c8d6b092 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -77,9 +77,9 @@ def check_axiom_1(cls, v: dict) -> dict: Axiom 1: ListLengthConsistency. AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length. """ - alias_list: List[str] = v.get("AboutNodeAliasList", None) - value_list: List[int] = v.get("ValueList", None) - tn_list: List[TelemetryName] = v.get("TelemetryNameList", None) + alias_list: List[str] = v.get("AboutNodeAliasList") + value_list: List[int] = v.get("ValueList") + tn_list: List[TelemetryName] = v.get("TelemetryNameList") if (len(value_list) != len(alias_list)) or (len(value_list) != len(tn_list)): raise ValueError( "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index 58b04fce..4fd3564e 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -73,7 +73,7 @@ def make_stub(cls, component_id): def from_component_id( cls, component_id: str, components: dict[str, Component] ) -> "HubitatComponent": - hubitat_component = components.get(component_id, None) + hubitat_component = components.get(component_id) if hubitat_component is None: raise ValueError( "ERROR. No component found for " diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 4b5fad76..530944c7 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -83,8 +83,8 @@ def check_axiom_1(cls, v: dict) -> dict: Axiom 1: Async reporting consistency. If AsyncReportThreshold exists, so does NameplateMaxValue """ - AsyncReportThreshold = v.get("AsyncReportThreshold", None) - NameplateMaxValue = v.get("NameplateMaxValue", None) + AsyncReportThreshold = v.get("AsyncReportThreshold") + NameplateMaxValue = v.get("NameplateMaxValue") if AsyncReportThreshold is not None: if NameplateMaxValue is None: raise ValueError( diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 959e6a0e..2920b7de 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -78,9 +78,9 @@ def check_axiom_1(cls, v: dict) -> dict: Axiom 1: ListLengthConsistency. AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length. """ - alias_list: List[str] = v.get("AboutNodeAliasList", None) - value_list: List[int] = v.get("ValueList", None) - tn_list: List[TelemetryName] = v.get("TelemetryNameList", None) + alias_list: List[str] = v.get("AboutNodeAliasList") + value_list: List[int] = v.get("ValueList") + tn_list: List[TelemetryName] = v.get("TelemetryNameList") if (len(value_list) != len(alias_list)) or (len(value_list) != len(tn_list)): raise ValueError( "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." From 60d27f4a242f1ed5a57522daee688808167d783e Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 10:44:14 -0400 Subject: [PATCH 042/168] ruff check --fix: fixes E713, E714, F541, PIE790, PLR0402, PLR2044, PLR5501, RUF100 --- src/gwproto/data_classes/errors.py | 4 --- src/gwproto/default_decoders.py | 22 ++++++------- src/gwproto/enums/actor_class.py | 2 +- src/gwproto/enums/local_comm_interface.py | 2 +- src/gwproto/enums/make_model.py | 2 +- src/gwproto/enums/role.py | 2 +- src/gwproto/enums/telemetry_name.py | 2 +- src/gwproto/enums/unit.py | 2 +- src/gwproto/errors.py | 8 ----- src/gwproto/gs/gs_dispatch_base.py | 2 +- src/gwproto/gs/gs_pwr_base.py | 4 +-- .../types/component_attribute_class_gt.py | 4 +-- src/gwproto/types/component_gt.py | 4 +-- .../types/electric_meter_component_gt.py | 6 ++-- src/gwproto/types/gt_dispatch_boolean.py | 2 +- .../types/gt_dispatch_boolean_local.py | 2 +- .../types/gt_driver_booleanactuator_cmd.py | 2 +- src/gwproto/types/hubitat_tank_gt.py | 32 +++++++++---------- src/gwproto/types/rest_poller_cac_gt.py | 4 +-- src/gwproto/types/rest_poller_component_gt.py | 4 +-- .../types/telemetry_reporting_config.py | 2 +- tests/types/test_hubitat_gt.py | 2 +- 22 files changed, 51 insertions(+), 65 deletions(-) diff --git a/src/gwproto/data_classes/errors.py b/src/gwproto/data_classes/errors.py index 61397172..fee027ff 100644 --- a/src/gwproto/data_classes/errors.py +++ b/src/gwproto/data_classes/errors.py @@ -1,10 +1,6 @@ class DcError(Exception): """Base class for dataclass errors""" - pass - class DataClassLoadingError(Exception): """data class loading error""" - - pass diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index cdae3605..4e67d557 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -2,17 +2,17 @@ import typing from typing import Type, TypeVar -import gwproto.types.fibaro_smart_implant_cac_gt # noqa -import gwproto.types.fibaro_smart_implant_component_gt # noqa -import gwproto.types.hubitat_cac_gt # noqa -import gwproto.types.hubitat_component_gt # noqa -import gwproto.types.hubitat_poller_cac_gt # noqa -import gwproto.types.hubitat_poller_component_gt # noqa -import gwproto.types.hubitat_tank_cac_gt # noqa -import gwproto.types.hubitat_tank_component_gt # noqa -import gwproto.types.rest_poller_cac_gt # noqa -import gwproto.types.rest_poller_component_gt # noqa -import gwproto.types.web_server_cac_gt # noqa +import gwproto.types.fibaro_smart_implant_cac_gt +import gwproto.types.fibaro_smart_implant_component_gt +import gwproto.types.hubitat_cac_gt +import gwproto.types.hubitat_component_gt +import gwproto.types.hubitat_poller_cac_gt +import gwproto.types.hubitat_poller_component_gt +import gwproto.types.hubitat_tank_cac_gt +import gwproto.types.hubitat_tank_component_gt +import gwproto.types.rest_poller_cac_gt +import gwproto.types.rest_poller_component_gt +import gwproto.types.web_server_cac_gt import gwproto.types.web_server_component_gt # noqa from gwproto.data_classes.component import Component from gwproto.data_classes.component_attribute_class import ComponentAttributeClass diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 418c296d..aa9a942d 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -106,7 +106,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 78041f1a..db44c4a7 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -69,7 +69,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index 5d9709d6..246db256 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -114,7 +114,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 39d87299..d42c6450 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -102,7 +102,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index 4bbe7fab..178554e6 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -91,7 +91,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 48603c0a..010ce1f6 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -73,7 +73,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] diff --git a/src/gwproto/errors.py b/src/gwproto/errors.py index acf3d372..b0962b44 100644 --- a/src/gwproto/errors.py +++ b/src/gwproto/errors.py @@ -1,22 +1,14 @@ class SchemaError(Exception): """Base class for Schema errors""" - pass - class AlgoError(Exception): """Base class for errors related to Algorand""" - pass - class DcError(Exception): """Base class for dataclass errors""" - pass - class RegistryError(Exception): """Base class for registry errors""" - - pass diff --git a/src/gwproto/gs/gs_dispatch_base.py b/src/gwproto/gs/gs_dispatch_base.py index bbba2894..70739dc3 100644 --- a/src/gwproto/gs/gs_dispatch_base.py +++ b/src/gwproto/gs/gs_dispatch_base.py @@ -3,7 +3,7 @@ import struct from typing import List, NamedTuple -import gwproto.property_format as property_format +from gwproto import property_format class GsDispatchBase(NamedTuple): diff --git a/src/gwproto/gs/gs_pwr_base.py b/src/gwproto/gs/gs_pwr_base.py index ad6dbec6..06b09317 100644 --- a/src/gwproto/gs/gs_pwr_base.py +++ b/src/gwproto/gs/gs_pwr_base.py @@ -3,10 +3,10 @@ import struct from typing import List, NamedTuple -import gwproto.property_format as property_format +from gwproto import property_format -class GsPwrBase(NamedTuple): # +class GsPwrBase(NamedTuple): Power: int TypeName: str = "p" diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index b14939ab..9f28fca5 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -205,11 +205,11 @@ def dc_to_tuple(cls, dc: ComponentAttributeClass) -> ComponentAttributeClassGt: @classmethod def type_to_dc(cls, t: str) -> ComponentAttributeClass: - return cls.tuple_to_dc(cls.type_to_tuple(t)) # noqa + return cls.tuple_to_dc(cls.type_to_tuple(t)) @classmethod def dc_to_type(cls, dc: ComponentAttributeClass) -> str: - return cls.dc_to_tuple(dc).as_type() # noqa + return cls.dc_to_tuple(dc).as_type() @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> ComponentAttributeClass: diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index d47b7391..ef078dd0 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -237,11 +237,11 @@ def dc_to_tuple(cls, dc: Component) -> ComponentGt: @classmethod def type_to_dc(cls, t: str) -> Component: - return cls.tuple_to_dc(cls.type_to_tuple(t)) # noqa + return cls.tuple_to_dc(cls.type_to_tuple(t)) @classmethod def dc_to_type(cls, dc: Component) -> str: - return cls.dc_to_tuple(dc).as_type() # noqa + return cls.dc_to_tuple(dc).as_type() @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> Component: diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 585b63aa..f44f2743 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -134,9 +134,9 @@ def check_axiom_1(cls, v: dict) -> dict: # TODO: Implement check for axiom 1" ModbusHost = v.get("ModbusHost") ModbusPort = v.get("ModbusHost") - if ModbusHost is None and not (ModbusPort is None): + if ModbusHost is None and ModbusPort is not None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") - if not (ModbusHost is None) and ModbusPort is None: + if ModbusHost is not None and ModbusPort is None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") return v @@ -156,7 +156,7 @@ def check_axiom_2(cls, v: dict) -> dict: if ModbusHost is None: raise ValueError( - f"Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist!" + "Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist!" ) output_configs = set(map(lambda x: x.OutputConfig, EgaugeIoList)) if output_configs != set(ConfigList): diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index f84aa9e0..908b94c1 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -267,7 +267,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if not v in [0, 1]: + if v not in [0, 1]: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 1563b29d..2ce64f06 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -234,7 +234,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if not v in [0, 1]: + if v not in [0, 1]: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 95a05895..05cb308a 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -210,7 +210,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if not v in [0, 1]: + if v not in [0, 1]: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index 564f4884..3126f973 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -140,25 +140,23 @@ def resolve_rest( poll_period_seconds=poll_period_seconds, request=RequestArgs(url=constructed_config), ) + elif self.rest.request.url is None: + self.rest.request.url = constructed_config else: - # Again, no inline url config is found; use constructed url config - if self.rest.request.url is None: - self.rest.request.url = constructed_config + # An inline config exists; take items *not* in inline config from + # constructed config (inline config 'wins' on disagreement) + existing_config = self.rest.request.url + if not existing_config.url_args.host: + existing_config.url_args.host = constructed_config.url_args.host + if existing_config.url_path_format is None: + existing_config.url_path_format = constructed_config.url_path_format + if existing_config.url_path_args is None: + existing_config.url_path_args = constructed_config.url_path_args else: - # An inline config exists; take items *not* in inline config from - # constructed config (inline config 'wins' on disagreement) - existing_config = self.rest.request.url - if not existing_config.url_args.host: - existing_config.url_args.host = constructed_config.url_args.host - if existing_config.url_path_format is None: - existing_config.url_path_format = constructed_config.url_path_format - if existing_config.url_path_args is None: - existing_config.url_path_args = constructed_config.url_path_args - else: - existing_config.url_path_args = dict( - constructed_config.url_path_args, - **existing_config.url_path_args, - ) + existing_config.url_path_args = dict( + constructed_config.url_path_args, + **existing_config.url_path_args, + ) self.rest.clear_property_cache() # Verify new URL produced by combining any inline REST configuration diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index 5ecc034b..2ed39541 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -41,7 +41,7 @@ def __init__(self, cac: RESTPollerCac): @classmethod def tuple_to_type(cls, tpl: RESTPollerCacGt) -> str: - return tpl.as_type() # noqa + return tpl.as_type() @classmethod def type_to_tuple(cls, t: str) -> RESTPollerCacGt: @@ -65,7 +65,7 @@ def type_to_dc(cls, t: str) -> RESTPollerCac: @classmethod def dc_to_type(cls, dc: RESTPollerCac) -> str: - return cls.dc_to_tuple(dc).as_type() # noqa + return cls.dc_to_tuple(dc).as_type() @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerCac: diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index 95688065..d3becc9a 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -55,7 +55,7 @@ def __init__(self, component: RESTPollerComponent): @classmethod def tuple_to_type(cls, tpl: RESTPollerComponentGt) -> str: - return tpl.as_type() # noqa + return tpl.as_type() @classmethod def type_to_tuple(cls, t: str) -> RESTPollerComponentGt: @@ -79,7 +79,7 @@ def type_to_dc(cls, t: str) -> RESTPollerComponent: @classmethod def dc_to_type(cls, dc: RESTPollerComponent) -> str: - return cls.dc_to_tuple(dc).as_type() # noqa + return cls.dc_to_tuple(dc).as_type() @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerComponent: diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 530944c7..b2b07246 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -88,7 +88,7 @@ def check_axiom_1(cls, v: dict) -> dict: if AsyncReportThreshold is not None: if NameplateMaxValue is None: raise ValueError( - f"Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" + "Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" ) return v diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index 4b7c47e0..2206723c 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -20,7 +20,7 @@ def test_hubitat_gt() -> None: h2 = HubitatGt(WebListenEnabled=False, **h.dict(exclude_unset=True)) assert h2.WebListenEnabled is False url = yarl.URL.build(scheme="http", host="192.168.1.20", port=1) - assert str(h.listen_url(url)) == f"http://192.168.1.20:1/{listen_path_exp}" # noqa + assert str(h.listen_url(url)) == f"http://192.168.1.20:1/{listen_path_exp}" assert h.url_config().to_url() == yarl.URL("http://192.168.1.10") assert h.maker_api_url_config().to_url() == yarl.URL( From fea6da2b331b90bc62f71b3830ea83f409cce760 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 11:15:08 -0400 Subject: [PATCH 043/168] ruff: suppress TRY003 for now; lots of them generated by code generation. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 904aba9a..f695d0a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -132,6 +132,7 @@ ignore = [ "ISC001", "PLR0904", "PLW1514", + "TRY003", # Suppress for now; lots of them generated by code generation. "W191", ] From f8270a38eb2d70744b0c9c3bb6afe6de61b4266f Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 12:16:42 -0400 Subject: [PATCH 044/168] Upgrade fastapi-utils to get version that supports pydantic 2 --- poetry.lock | 208 +++++++++++-------------------------------------- pyproject.toml | 2 +- 2 files changed, 47 insertions(+), 163 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3787cde2..9773b311 100644 --- a/poetry.lock +++ b/poetry.lock @@ -359,19 +359,23 @@ standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt [[package]] name = "fastapi-utils" -version = "0.2.1" +version = "0.7.0" description = "Reusable utilities for FastAPI" optional = false -python-versions = ">=3.6,<4.0" +python-versions = "<4.0,>=3.7" files = [ - {file = "fastapi-utils-0.2.1.tar.gz", hash = "sha256:0e6c7fc1870b80e681494957abf65d4f4f42f4c7f70005918e9181b22f1bd759"}, - {file = "fastapi_utils-0.2.1-py3-none-any.whl", hash = "sha256:dd0be7dc7f03fa681b25487a206651d99f2330d5a567fb8ab6cb5f8a06a29360"}, + {file = "fastapi_utils-0.7.0-py3-none-any.whl", hash = "sha256:4fc4d6a10b5c5c3f2ec564d360fc1188507b911e4b06ee4d4c111906d7ddeef1"}, + {file = "fastapi_utils-0.7.0.tar.gz", hash = "sha256:074509405b02e2651dfe2d11862dd760bacc1a64508f3d8cc44e52a6dc1ed342"}, ] [package.dependencies] -fastapi = "*" -pydantic = ">=1.0,<2.0" -sqlalchemy = ">=1.3.12,<2.0.0" +fastapi = ">=0.89,<1.0" +psutil = ">=5,<6" +pydantic = ">1.0,<3.0" + +[package.extras] +all = ["pydantic-settings (>=2.0.1,<3.0.0)", "sqlalchemy (>=1.4,<3.0)", "typing-inspect (>=0.9.0,<0.10.0)"] +session = ["sqlalchemy (>=1.4,<3.0)"] [[package]] name = "filelock" @@ -422,77 +426,6 @@ pygments = ">=2.7" sphinx = ">=6.0,<9.0" sphinx-basic-ng = ">=1.0.0.beta2" -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - [[package]] name = "h11" version = "0.14.0" @@ -1019,6 +952,34 @@ files = [ "ruamel.yaml" = ">=0.15" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "pycodestyle" version = "2.12.1" @@ -1426,13 +1387,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] @@ -1619,83 +1580,6 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] -[[package]] -name = "sqlalchemy" -version = "1.4.53" -description = "Database Abstraction Library" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "SQLAlchemy-1.4.53-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b61ac5457d91b5629a3dea2b258deb4cdd35ac8f6fa2031d2b9b2fff5b3396da"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a96aa8d425047551676b0e178ddb0683421e78eda879ab55775128b2e612cae"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e10ac36f0b994235c13388b39598bf27219ec8bdea5be99bdac612b01cbe525"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:437592b341a3229dd0443c9c803b0bf0a466f8f539014fef6cdb9c06b7edb7f9"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:784272ceb5eb71421fea9568749bcbe8bd019261a0e2e710a7efa76057af2499"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-win32.whl", hash = "sha256:122d7b5722df1a24402c6748bbb04687ef981493bb559d0cc0beffe722e0e6ed"}, - {file = "SQLAlchemy-1.4.53-cp310-cp310-win_amd64.whl", hash = "sha256:4604d42b2abccba266d3f5bbe883684b5df93e74054024c70d3fbb5eea45e530"}, - {file = "SQLAlchemy-1.4.53-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fb8e15dfa47f5de11ab073e12aadd6b502cfb7ac4bafd18bd18cfd1c7d13dbbc"}, - {file = "SQLAlchemy-1.4.53-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8be4df55e8fde3006d9cb1f6b3df2ba26db613855dc4df2c0fcd5ec15cb3b7"}, - {file = "SQLAlchemy-1.4.53-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b11640251f9a9789fd96cd6e5d176b1c230230c70ad40299bcbcc568451b4c"}, - {file = "SQLAlchemy-1.4.53-cp311-cp311-win32.whl", hash = "sha256:cd534c716f86bdf95b7b984a34ee278c91d1b1d7d183e7e5ff878600b1696046"}, - {file = "SQLAlchemy-1.4.53-cp311-cp311-win_amd64.whl", hash = "sha256:6dd06572872ca13ef5a90306a3e5af787498ddaa17fb00109b1243642646cd69"}, - {file = "SQLAlchemy-1.4.53-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2774c24c405136c3ef472e2352bdca7330659d481fbf2283f996c0ef9eb90f22"}, - {file = "SQLAlchemy-1.4.53-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68a614765197b3d13a730d631a78c3bb9b3b72ba58ed7ab295d58d517464e315"}, - {file = "SQLAlchemy-1.4.53-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d13d4dfbc6e52363886b47cf02cf68c5d2a37c468626694dc210d7e97d4ad330"}, - {file = "SQLAlchemy-1.4.53-cp312-cp312-win32.whl", hash = "sha256:197065b91456574d70b6459bfa62bc0b52a4960a29ef923c375ec427274a3e05"}, - {file = "SQLAlchemy-1.4.53-cp312-cp312-win_amd64.whl", hash = "sha256:421306c4b936b0271a3ce2dc074928d5ece4a36f9c482daa5770f44ecfc3a883"}, - {file = "SQLAlchemy-1.4.53-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:13fc34b35d8ddb3fbe3f8fcfdf6c2546e676187f0fb20f5774da362ddaf8fa2d"}, - {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626be971ff89541cfd3e70b54be00b57a7f8557204decb6223ce0428fec058f3"}, - {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:991e42fdfec561ebc6a4fae7161a86d129d6069fa14210b96b8dd752afa7059c"}, - {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95123f3a1e0e8020848fd32ba751db889a01a44e4e4fef7e58c87ddd0b2fca59"}, - {file = "SQLAlchemy-1.4.53-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c58e011e9e6373b3a091d83f20601fb335a3b4bace80bfcb914ac168aad3b70d"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:670c7769bf5dcae9aff331247b5d82fe635c63731088a46ce68ba2ba519ef36e"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07ba54f09033d387ae9df8d62cbe211ed7304e0bfbece1f8c55e21db9fae5c11"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a38834b4c183c33daf58544281395aad2e985f0b47cca1e88ea5ada88344e63"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:616492f5315128a847f293a7c552f3561ac7e996d2aa5dc46bef4fb0d3781f1d"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0cf8c0af9563892c6632f7343bc393dfce6eeef8e4d10c5fadba9c0390520bd"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-win32.whl", hash = "sha256:c05fe05941424c2f3747a8952381b7725e24cba2ca00141380e54789d5b616b6"}, - {file = "SQLAlchemy-1.4.53-cp37-cp37m-win_amd64.whl", hash = "sha256:93e90aa3e3b2f8e8cbae4d5509f8e0cf82972378d323c740a8df1c1e9f484172"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:9d7368df54d3ed45a18955f6cec38ebe075290594ac0d5c87a8ddaff7e10de27"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89d8ac4158ef68eea8bb0f6dd0583127d9aa8720606964ba8eee20b254f9c83a"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16bb9fa4d00b4581b14d9f0e2224dc7745b854aa4687738279af0f48f7056c98"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fe5168d0249c23f537950b6d75935ff2709365a113e29938a979aec36668ecf"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8608d162d3bd29d807aab32c3fb6e2f8e225a43d1c54c917fed38513785380"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-win32.whl", hash = "sha256:a9d4d132198844bd6828047135ce7b887687c92925049a2468a605fc775c7a1a"}, - {file = "SQLAlchemy-1.4.53-cp38-cp38-win_amd64.whl", hash = "sha256:c15d1f1fcf1f9bec0499ae1d9132b950fcc7730f2d26d10484c8808b4e077816"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:edf094a20a386ff2ec73de65ef18014b250259cb860edc61741e240ca22d6981"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a9c3514ff19d9d30d8a8d378b24cd1dfa5528d20891481cb5f196117db6a48"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaaeedbceb4dfd688fff2faf25a9a87a391f548811494f7bff7fa701b639abc3"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d021699b9007deb7aa715629078830c99a5fec2753d9bdd5ff33290d363ef755"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0465b8a68f8f4de754c1966c45b187ac784ad97bc9747736f913130f0e1adea0"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-win32.whl", hash = "sha256:5f67b9e9dcac3241781e96575468d55a42332157dee04bdbf781df573dff5f85"}, - {file = "SQLAlchemy-1.4.53-cp39-cp39-win_amd64.whl", hash = "sha256:a8c2f2a0b2c4e3b86eb58c9b6bb98548205eea2fba9dae4edfd29dc6aebbe95a"}, - {file = "SQLAlchemy-1.4.53.tar.gz", hash = "sha256:5e6ab710c4c064755fd92d1a417bef360228a19bdf0eee32b03aa0f5f8e9fe0d"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)", "mariadb (>=1.0.1,!=1.1.2)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql", "pymssql"] -mssql-pyodbc = ["pyodbc", "pyodbc"] -mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysql-connector-python", "mysql-connector-python"] -oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "asyncpg", "greenlet (!=0.4.17)", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)", "pg8000 (>=1.16.6,!=1.29.0)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3_binary"] - [[package]] name = "starlette" version = "0.37.2" @@ -1794,13 +1678,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.30.5" +version = "0.30.6" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"}, - {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"}, + {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, + {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, ] [package.dependencies] @@ -2144,4 +2028,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "ed33d4125be6399c1a3c1b9da5f4fe47040a8afbb145936290256bdd0c165fe5" +content-hash = "7d803c01a0ef47a3e7727e06a8143c2e283f279d04c299cde985c938f1519ba2" diff --git a/pyproject.toml b/pyproject.toml index f695d0a2..de274f40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ python = "^3.10" pydantic = "^1.10.11" yarl = "^1.9.2" pytz = "^2024.1" -fastapi-utils = "^0.2.1" +fastapi-utils = "^0.7.0" pendulum = "2.1.2" ruff = "^0.5.7" From eb8427a6ddc012163f2f31ca851630c7f4e6ae28 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 12:18:22 -0400 Subject: [PATCH 045/168] Add bump-pydantic --- poetry.lock | 87 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 9773b311..35d272b9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -68,6 +68,23 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "bump-pydantic" +version = "0.8.0" +description = "Convert Pydantic from V1 to V2 â™»" +optional = false +python-versions = ">=3.8" +files = [ + {file = "bump_pydantic-0.8.0-py3-none-any.whl", hash = "sha256:6cbb4deb5869a69baa5a477f28f3e2d8fb09b687e114c018bd54470590ae7bf7"}, + {file = "bump_pydantic-0.8.0.tar.gz", hash = "sha256:6092e61930e85619e74eeb04131b4387feda16f02d8bb2e3cf9507fa492c69e9"}, +] + +[package.dependencies] +libcst = ">=0.4.2" +rich = "*" +typer = ">=0.7.0" +typing-extensions = "*" + [[package]] name = "certifi" version = "2024.7.4" @@ -501,6 +518,46 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "libcst" +version = "1.4.0" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "libcst-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:279b54568ea1f25add50ea4ba3d76d4f5835500c82f24d54daae4c5095b986aa"}, + {file = "libcst-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3401dae41fe24565387a65baee3887e31a44e3e58066b0250bc3f3ccf85b1b5a"}, + {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1989fa12d3cd79118ebd29ebe2a6976d23d509b1a4226bc3d66fcb7cb50bd5d"}, + {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addc6d585141a7677591868886f6bda0577529401a59d210aa8112114340e129"}, + {file = "libcst-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d71001cb25e94cfe8c3d997095741a8c4aa7a6d234c0f972bc42818c88dfaf"}, + {file = "libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3"}, + {file = "libcst-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6227562fc5c9c1efd15dfe90b0971ae254461b8b6b23c1b617139b6003de1c1"}, + {file = "libcst-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3399e6c95df89921511b44d8c5bf6a75bcbc2d51f1f6429763609ba005c10f6b"}, + {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48601e3e590e2d6a7ab8c019cf3937c70511a78d778ab3333764531253acdb33"}, + {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42797309bb725f0f000510d5463175ccd7155395f09b5e7723971b0007a976d"}, + {file = "libcst-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb4e42ea107a37bff7f9fdbee9532d39f9ea77b89caa5c5112b37057b12e0838"}, + {file = "libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9"}, + {file = "libcst-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7ece51d935bc9bf60b528473d2e5cc67cbb88e2f8146297e40ee2c7d80be6f13"}, + {file = "libcst-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:81653dea1cdfa4c6520a7c5ffb95fa4d220cbd242e446c7a06d42d8636bfcbba"}, + {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6abce0e66bba2babfadc20530fd3688f672d565674336595b4623cd800b91ef"}, + {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da9d7dc83801aba3b8d911f82dc1a375db0d508318bad79d9fb245374afe068"}, + {file = "libcst-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c54aa66c86d8ece9c93156a2cf5ca512b0dce40142fe9e072c86af2bf892411"}, + {file = "libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654"}, + {file = "libcst-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8ecdba8934632b4dadacb666cd3816627a6ead831b806336972ccc4ba7ca0e9"}, + {file = "libcst-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e54c777b8d27339b70f304d16fc8bc8674ef1bd34ed05ea874bf4921eb5a313"}, + {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:061d6855ef30efe38b8a292b7e5d57c8e820e71fc9ec9846678b60a934b53bbb"}, + {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0abf627ee14903d05d0ad9b2c6865f1b21eb4081e2c7bea1033f85db2b8bae"}, + {file = "libcst-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d024f44059a853b4b852cfc04fec33e346659d851371e46fc8e7c19de24d3da9"}, + {file = "libcst-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c6a8faab9da48c5b371557d0999b4ca51f4f2cbd37ee8c2c4df0ac01c781465"}, + {file = "libcst-1.4.0.tar.gz", hash = "sha256:449e0b16604f054fa7f27c3ffe86ea7ef6c409836fe68fe4e752a1894175db00"}, +] + +[package.dependencies] +pyyaml = ">=5.2" + +[package.extras] +dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.6)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.6.0)", "usort (==1.0.8.post1)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -1352,6 +1409,17 @@ files = [ {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, ] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "six" version = "1.16.0" @@ -1637,6 +1705,23 @@ typing-extensions = ">=4.10.0" doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)"] test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + [[package]] name = "types-pytz" version = "2024.1.0.20240417" @@ -2028,4 +2113,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "7d803c01a0ef47a3e7727e06a8143c2e283f279d04c299cde985c938f1519ba2" +content-hash = "60006c3ce561e7c1b21dc8a48e34510500b22568ece2e0e44b4de550fe987976" diff --git a/pyproject.toml b/pyproject.toml index de274f40..5711864e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ xdoctest = {extras = ["colors"], version = ">=0.15.10"} myst-parser = {version = ">=0.16.1"} types-pytz = ">=2022.4.0.0" rich = ">=12.6.0" +bump-pydantic = "^0.8.0" [tool.coverage.paths] source = ["src", "*/site-packages"] From f6de3e0904d4ff7b38d7bb02387f574ae163a7e4 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 13:12:23 -0400 Subject: [PATCH 046/168] Temporary hacking of a few Literal strings to work around bump-pydantic bug --- src/gwproto/types/component_attribute_class_gt.py | 3 ++- src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py | 4 ++-- src/gwproto/types/heartbeat_b.py | 4 ++-- tests/types/test_component_attribute_class_gt.py | 3 ++- tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py | 3 ++- tests/types/test_heartbeat_b.py | 3 ++- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 9f28fca5..af9f3bde 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -45,7 +45,8 @@ class ComponentAttributeClassGt(BaseModel): ), default=None, ) - TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" + # TODO: bump-pydnatic hack; restore this to "component.attribute.class.gt" + TypeName: Literal["component.attribute.CLASS.gt"] = "component.attribute.CLASS.gt" Version: Literal["000"] = "000" @validator("ComponentAttributeClassId") diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index c8d6b092..0123c641 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -45,8 +45,8 @@ class GtShTelemetryFromMultipurposeSensor(BaseModel): ValueList: List[int] = Field( title="ValueList", ) - TypeName: Literal["gt.sh.telemetry.from.multipurpose.sensor"] = ( - "gt.sh.telemetry.from.multipurpose.sensor" + TypeName: Literal["gt.sh.telemetry.FROM.multipurpose.sensor"] = ( + "gt.sh.telemetry.FROM.multipurpose.sensor" # TODO: bump-pydnatic hack; restore this to "gt.sh.telemetry.from.multipurpose.sensor" ) Version: Literal["100"] = "100" diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index f649b5ad..60009a96 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -53,7 +53,7 @@ class HeartbeatB(BaseModel): ), ) TypeName: Literal["heartbeat.b"] = "heartbeat.b" - Version: Literal["001"] = "001" + Version: Literal["_001"] = "_001" # TODO: bump-pydnatic hack; restore this to "000" @validator("FromGNodeAlias") def _check_from_g_node_alias(cls, v: str) -> str: @@ -251,7 +251,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: raise SchemaError(f"TypeName missing from dict <{d2}>") if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "001": + if d2["Version"] != "_001": # TODO: bump-pydnatic hack; restore this to "000" LOGGER.debug( f"Attempting to interpret heartbeat.b version {d2['Version']} as version 001" ) diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 06f274da..88d6c26b 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -9,10 +9,11 @@ def test_component_attribute_class_gt_generated() -> None: + # TODO: bump-pydnatic hack; restore this to "component.attribute.class.gt" d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", - "TypeName": "component.attribute.class.gt", + "TypeName": "component.attribute.CLASS.gt", "Version": "000", } diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index adbdd693..08be0096 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -14,7 +14,8 @@ def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: "AboutNodeAliasList": ["a.elt1"], "TelemetryNameList": ["ad19e79c"], "ValueList": [18000], - "TypeName": "gt.sh.telemetry.from.multipurpose.sensor", + # TODO: bump-pydnatic hack; restore this to "gt.sh.telemetry.from.multipurpose.sensor" + "TypeName": "gt.sh.telemetry.FROM.multipurpose.sensor", "Version": "100", } diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index b1bb0e1c..b3099a8f 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -18,7 +18,8 @@ def test_heartbeat_b_generated() -> None: "SendTimeUnixMs": 1673635765317, "StartingOver": False, "TypeName": "heartbeat.b", - "Version": "001", + # TODO: bump-pydnatic hack; restore this to "000" + "Version": "_001", } with pytest.raises(SchemaError): From b23acff90a1011e7f638bedaaa6d21a1680a901c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 13:48:52 -0400 Subject: [PATCH 047/168] bump-pydantic, BP003 --- src/gwproto/message.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gwproto/message.py b/src/gwproto/message.py index 60b8458c..70b27514 100644 --- a/src/gwproto/message.py +++ b/src/gwproto/message.py @@ -1,6 +1,6 @@ -from typing import Any, Callable, Generic, Mapping, Optional, TypeVar, Union +from typing import Any, Callable, Generic, Literal, Mapping, Optional, TypeVar, Union -from pydantic import BaseModel, Field +from pydantic import BaseModel from pydantic.generics import GenericModel from gwproto.topic import MQTTTopic @@ -23,7 +23,7 @@ class Header(BaseModel): MessageType: str MessageId: str = "" AckRequired: bool = False - TypeName: str = Field("gridworks.header", const=True) + TypeName: Literal["gridworks.header"] = "gridworks.header" PayloadT = TypeVar("PayloadT") @@ -43,7 +43,7 @@ def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict) -> None: class Message(GenericModel, Generic[PayloadT]): Header: Header Payload: PayloadT - TypeName: str = Field(GRIDWORKS_ENVELOPE_TYPE, const=True) + TypeName: Literal[GRIDWORKS_ENVELOPE_TYPE] = GRIDWORKS_ENVELOPE_TYPE def __init__(self, header: Optional[Header] = None, **kwargs: Any): if header is None: From caaa336ffe34b9fbf1165cdd65430d3727b549b8 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 14:51:14 -0400 Subject: [PATCH 048/168] Remove fastapi-utils since it still seems to be fighting --- poetry.lock | 70 +---------------------- pyproject.toml | 5 +- src/gwproto/enums/actor_class.py | 2 +- src/gwproto/enums/better_str_enum.py | 13 +++++ src/gwproto/enums/local_comm_interface.py | 2 +- src/gwproto/enums/make_model.py | 2 +- src/gwproto/enums/role.py | 2 +- src/gwproto/enums/telemetry_name.py | 2 +- src/gwproto/enums/unit.py | 2 +- 9 files changed, 23 insertions(+), 77 deletions(-) create mode 100644 src/gwproto/enums/better_str_enum.py diff --git a/poetry.lock b/poetry.lock index 35d272b9..a1387bf6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -354,46 +354,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "fastapi" -version = "0.112.0" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.112.0-py3-none-any.whl", hash = "sha256:3487ded9778006a45834b8c816ec4a48d522e2631ca9e75ec5a774f1b052f821"}, - {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "fastapi-utils" -version = "0.7.0" -description = "Reusable utilities for FastAPI" -optional = false -python-versions = "<4.0,>=3.7" -files = [ - {file = "fastapi_utils-0.7.0-py3-none-any.whl", hash = "sha256:4fc4d6a10b5c5c3f2ec564d360fc1188507b911e4b06ee4d4c111906d7ddeef1"}, - {file = "fastapi_utils-0.7.0.tar.gz", hash = "sha256:074509405b02e2651dfe2d11862dd760bacc1a64508f3d8cc44e52a6dc1ed342"}, -] - -[package.dependencies] -fastapi = ">=0.89,<1.0" -psutil = ">=5,<6" -pydantic = ">1.0,<3.0" - -[package.extras] -all = ["pydantic-settings (>=2.0.1,<3.0.0)", "sqlalchemy (>=1.4,<3.0)", "typing-inspect (>=0.9.0,<0.10.0)"] -session = ["sqlalchemy (>=1.4,<3.0)"] - [[package]] name = "filelock" version = "3.15.4" @@ -1009,34 +969,6 @@ files = [ "ruamel.yaml" = ">=0.15" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -[[package]] -name = "psutil" -version = "5.9.8" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - [[package]] name = "pycodestyle" version = "2.12.1" @@ -2113,4 +2045,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "60006c3ce561e7c1b21dc8a48e34510500b22568ece2e0e44b4de550fe987976" +content-hash = "49681d30d2dc2bcacab78493a5e9a77aac688b01c996563be1b7ca41995174ed" diff --git a/pyproject.toml b/pyproject.toml index 5711864e..63e807e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,6 @@ python = "^3.10" pydantic = "^1.10.11" yarl = "^1.9.2" pytz = "^2024.1" -fastapi-utils = "^0.7.0" pendulum = "2.1.2" ruff = "^0.5.7" @@ -116,7 +115,7 @@ exclude = [ "dist", ] output-format = "concise" -fix = false +fix = true show-fixes = true [tool.ruff.lint] @@ -126,7 +125,9 @@ select = ["ALL"] ignore = [ "B027", "COM812", + "CPY", "D", + "DOC", "E501", "EM", "FA", # We only support Python >= 3.10, so we shouldn't need this diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index aa9a942d..83c6ffde 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class ActorClass(StrEnum): diff --git a/src/gwproto/enums/better_str_enum.py b/src/gwproto/enums/better_str_enum.py new file mode 100644 index 00000000..364a3c98 --- /dev/null +++ b/src/gwproto/enums/better_str_enum.py @@ -0,0 +1,13 @@ +from enum import StrEnum +from typing import Any + + +class BetterStrEnum(StrEnum): + @staticmethod + def _generate_next_value_( + name: str, + start: int, # noqa: ARG004 + count: int, # noqa: ARG004 + last_values: list[Any], # noqa: ARG004 + ) -> str: + return name diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index db44c4a7..7a22cbed 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class LocalCommInterface(StrEnum): diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index 246db256..8f1af216 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class MakeModel(StrEnum): diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index d42c6450..1a832ba0 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class Role(StrEnum): diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index 178554e6..a19cac56 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class TelemetryName(StrEnum): diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 010ce1f6..fc273aba 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -1,7 +1,7 @@ from enum import auto from typing import List -from fastapi_utils.enums import StrEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum class Unit(StrEnum): From 158e0c2c2e0c253fd0c34f751a7603b9a47a1d18 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 14:51:56 -0400 Subject: [PATCH 049/168] Update to pydantic2 - tests fail --- poetry.lock | 175 +++++++++++++++++++++++++++++++++++-------------- pyproject.toml | 2 +- 2 files changed, 126 insertions(+), 51 deletions(-) diff --git a/poetry.lock b/poetry.lock index a1387bf6..ef893877 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,6 +11,17 @@ files = [ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] +[[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 = "anyio" version = "4.4.0" @@ -982,62 +993,126 @@ files = [ [[package]] name = "pydantic" -version = "1.10.17" -description = "Data validation and settings management using python type hints" +version = "2.8.2" +description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, - {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, - {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, - {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, - {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, - {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, - {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, - {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, - {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, - {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, - {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.20.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pyflakes" @@ -2045,4 +2120,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "49681d30d2dc2bcacab78493a5e9a77aac688b01c996563be1b7ca41995174ed" +content-hash = "48936a313a55194cc09311246fbfaeb79219b42db0cbbc12966f08757d48130e" diff --git a/pyproject.toml b/pyproject.toml index 63e807e7..f17194b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ Changelog = "https://github.com/thegridelectric/gridworks-protocol/releases" [tool.poetry.dependencies] python = "^3.10" -pydantic = "^1.10.11" +pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" pendulum = "2.1.2" From 7ca21001954c50d777d5b1156aa7538fa206832f Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 14 Aug 2024 14:55:58 -0400 Subject: [PATCH 050/168] Remove python 3.10 support, since python 3.10 does not have StrEnum --- .github/workflows/tests.yml | 1 - noxfile.py | 2 +- pyproject.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bda17a39..62b275ff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,6 @@ jobs: include: - { python: "3.11", os: "ubuntu-latest", session: "pre-commit" } # - { python: "3.12", os: "ubuntu-latest", session: "mypy" } - - { python: "3.10", os: "ubuntu-latest", session: "tests" } - { python: "3.11", os: "ubuntu-latest", session: "tests" } # - { python: "3.12", os: "ubuntu-latest", session: "tests" } # - { python: "3.12", os: "ubuntu-latest", session: "tests" } diff --git a/noxfile.py b/noxfile.py index 5b85ec89..eb5ee113 100644 --- a/noxfile.py +++ b/noxfile.py @@ -25,7 +25,7 @@ package = "gwproto" -python_versions = ["3.11", "3.10"] +python_versions = ["3.11"] nox.needs_version = ">= 2021.6.6" nox.options.sessions = ( diff --git a/pyproject.toml b/pyproject.toml index f17194b5..3a59b3a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ Changelog = "https://github.com/thegridelectric/gridworks-protocol/releases" [tool.poetry.dependencies] -python = "^3.10" +python = "^3.11" pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" From c06a8fbe8fd52e71d29e8ef4d8bbf97b289b148b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 15 Aug 2024 14:48:42 -0400 Subject: [PATCH 051/168] Use model_validator in place of root_validator --- .../types/electric_meter_component_gt.py | 38 ++++++++----------- .../gt_sh_multipurpose_telemetry_status.py | 14 +++---- .../types/gt_sh_simple_telemetry_status.py | 12 +++--- ...t_sh_telemetry_from_multipurpose_sensor.py | 17 +++++---- src/gwproto/types/rest_poller_gt.py | 27 +++++++------ .../types/telemetry_reporting_config.py | 21 +++++----- .../types/telemetry_snapshot_spaceheat.py | 19 +++++----- 7 files changed, 67 insertions(+), 81 deletions(-) diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index f44f2743..ee3c78bc 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -2,9 +2,9 @@ import json import logging -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional, Self -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, @@ -125,46 +125,38 @@ def _check_modbus_port(cls, v: Optional[int]) -> Optional[int]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> Self: """ Axiom 1: Modbus consistency. ModbusHost is None if and only if ModbusPort is None """ - # TODO: Implement check for axiom 1" - ModbusHost = v.get("ModbusHost") - ModbusPort = v.get("ModbusHost") - if ModbusHost is None and ModbusPort is not None: + if self.ModbusHost is None and self.ModbusPort is not None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") - if ModbusHost is not None and ModbusPort is None: + if self.ModbusHost is not None and self.ModbusPort is None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") - return v + return self - @root_validator - def check_axiom_2(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_2(self) -> Self: """ Axiom 2: Egauge4030 consistency. If the EgaugeIoList has non-zero length, then the ModbusHost is not None and the set of output configs is equal to ConfigList as a set """ - # TODO: Implement check for axiom 2" - EgaugeIoList = v.get("EgaugeIoList") - ModbusHost = v.get("ModbusHost") - ConfigList = v.get("ConfigList") - if len(EgaugeIoList) == 0: - return v + if len(self.EgaugeIoList) == 0: + return self - if ModbusHost is None: + if self.ModbusHost is None: raise ValueError( "Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist!" ) - output_configs = set(map(lambda x: x.OutputConfig, EgaugeIoList)) - if output_configs != set(ConfigList): + if {x.OutputConfig for x in self.EgaugeIoList} != set(self.ConfigList): raise ValueError( - "Axiom 2: If EgaugeIoList has non-zero length then then the set of" + "Axiom 2: If EgaugeIoList has non-zero length then the set of" "output configs must equal ConfigList as a set" ) - return v + return self def as_dict(self) -> Dict[str, Any]: """ diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index f15a5174..c93f28ec 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -2,9 +2,9 @@ import json import logging -from typing import Any, Dict, List, Literal +from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.errors import SchemaError @@ -85,19 +85,17 @@ def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> Self: """ Axiom 1: ListLengthConsistency. ValueList and ReadTimeUnixMsList must have the same length. """ - value_list: List[int] = v.get("ValueList") - time_list: List[int] = v.get("ReadTimeUnixMsList") - if len(value_list) != len(time_list): + if len(self.ValueList) != len(self.ReadTimeUnixMsList): raise ValueError( "Axiom 1: ValueList and ReadTimeUnixMsList must have the same length." ) - return v + return self def as_dict(self) -> Dict[str, Any]: """ diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 138d9e04..77cfecca 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.errors import SchemaError @@ -72,19 +72,17 @@ def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> dict: """ Axiom 1: ListLengthConsistency. ValueList and ReadTimeUnixMsList must have the same length. """ - value_list: List[int] = v.get("ValueList") - time_list: List[int] = v.get("ReadTimeUnixMsList") - if len(value_list) != len(time_list): + if len(self.ValueList) != len(self.ReadTimeUnixMsList): raise ValueError( "Axiom 1: ValueList and ReadTimeUnixMsList must have the same length." ) - return v + return self def as_dict(self) -> Dict[str, Any]: """ diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 0123c641..260ed789 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -2,9 +2,9 @@ import json import logging -from typing import Any, Dict, List, Literal +from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError @@ -71,16 +71,17 @@ def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> Self: """ Axiom 1: ListLengthConsistency. AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length. """ - alias_list: List[str] = v.get("AboutNodeAliasList") - value_list: List[int] = v.get("ValueList") - tn_list: List[TelemetryName] = v.get("TelemetryNameList") - if (len(value_list) != len(alias_list)) or (len(value_list) != len(tn_list)): + if not ( + len(self.ValueList) + == len(self.AboutNodeAliasList) + == len(self.TelemetryNameList) + ): raise ValueError( "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." ) diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index 4b61d5c6..b29984b8 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -4,10 +4,10 @@ """ from functools import cached_property -from typing import Literal, Optional, Tuple +from typing import Literal, Optional, Self, Tuple import yarl -from pydantic import BaseModel, Extra, HttpUrl, root_validator +from pydantic import BaseModel, Extra, HttpUrl, model_validator from gwproto.utils import snake_to_camel @@ -96,7 +96,7 @@ def make_url_args(cls, url_config: "URLConfig") -> Optional[dict]: # args from self.url if url_config.url is None: - url_args = dict() + url_args = {} else: url_args = dict(URLArgs.from_url(yarl.URL(url_config.url))) @@ -226,20 +226,19 @@ def url(self) -> yarl.URL: def clear_property_cache(self): self.__dict__.pop("url", None) - @root_validator(skip_on_failure=True) - def post_root_validator(cls, values: dict) -> dict: - base_url = URLConfig.make_url(values["session"].base_url) - url = URLConfig.make_url(values["request"].url) + @model_validator(mode="after") + def post_model_validator(self) -> Self: + base_url = URLConfig.make_url(self.session.base_url) + url = URLConfig.make_url(self.request.url) if base_url is None and url is None: raise ValueError( "ERROR. At least one of session.base_url and request.url must be specified" ) - if base_url is None: - if not url.is_absolute(): - raise ValueError( - "ERROR. if session.base_url is None, request.url must be absolute\n" - f" request.url: <{url}>\n" - ) + if base_url is None and not url.is_absolute(): + raise ValueError( + "ERROR. if session.base_url is None, request.url must be absolute\n" + f" request.url: <{url}>\n" + ) if base_url is not None and not base_url.is_absolute(): raise ValueError( f"ERROR. session.base_url is not absolute.\n" @@ -259,4 +258,4 @@ def post_root_validator(cls, values: dict) -> dict: f" request.url: <{url}>\n" f" request.url.path: <{url.path}>\n" ) - return values + return self diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index b2b07246..312b3e8d 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -2,9 +2,9 @@ import json import logging -from typing import Any, Dict, Literal, Optional +from typing import Any, Dict, Literal, Optional, Self -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.enums import Unit as EnumUnit @@ -77,20 +77,17 @@ def _check_nameplate_max_value(cls, v: Optional[int]) -> Optional[int]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> Self: """ Axiom 1: Async reporting consistency. If AsyncReportThreshold exists, so does NameplateMaxValue """ - AsyncReportThreshold = v.get("AsyncReportThreshold") - NameplateMaxValue = v.get("NameplateMaxValue") - if AsyncReportThreshold is not None: - if NameplateMaxValue is None: - raise ValueError( - "Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" - ) - return v + if self.AsyncReportThreshold is not None and self.NameplateMaxValue is None: + raise ValueError( + "Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" + ) + return self def as_dict(self) -> Dict[str, Any]: """ diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 2920b7de..f2234338 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -2,9 +2,9 @@ import json import logging -from typing import Any, Dict, List, Literal +from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, model_validator, validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError @@ -72,20 +72,21 @@ def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: ) return v - @root_validator - def check_axiom_1(cls, v: dict) -> dict: + @model_validator(mode="after") + def check_axiom_1(self) -> Self: """ Axiom 1: ListLengthConsistency. AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length. """ - alias_list: List[str] = v.get("AboutNodeAliasList") - value_list: List[int] = v.get("ValueList") - tn_list: List[TelemetryName] = v.get("TelemetryNameList") - if (len(value_list) != len(alias_list)) or (len(value_list) != len(tn_list)): + if not ( + len(self.ValueList) + == len(self.AboutNodeAliasList) + == len(self.TelemetryNameList) + ): raise ValueError( "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." ) - return v + return self def as_dict(self) -> Dict[str, Any]: """ From 91007b9ee88bba41126127876c943d6af3106187 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 18:36:24 -0400 Subject: [PATCH 052/168] Made tests pass - ran 'bump-pydantic src' and 'bump-pydantic tests' - Updated deprecated, changed or removed functionality: - BaseModel.parse_obj to BaseModel.model_validate - BaseModel.json to BaseModel.model_dump_json - BaseModel.dict to BaseModel.model_dump - BaseModel.copy to BaseModel.model_copy - BaseModel.__fields__ to BaseModel.model_fields - BaseModel.__fields_set__ to BaseModel.model_fields_set - GenericModel usage removed - Extra.allow to allow - validator to field_valiator - 'pre' validators to 'before' validators - Fixed 'unrecognized event' handling for (undocumented?) changed in pydantic ValidationError fields - Fixed discovery of payload types to explicitly exclude TypeName=='gw' since that now appears multiple times for classes derived from Message and in unpredictable order. Possibly this was always broken, we just had a different stable order previously that happened to work. In decoders.py, pydantic_named_types(), create_message_payload_discriminator(), create_discriminator(), - Fixed dynamic-discriminated-union construction in PydanticTypeNameDecoder() for changes in pydantic; __fields__ and sub_fields_mapping are gone and replaced by model_fields, annotation and typing.get_args. - cleaned up various warnings for sanity --- pyproject.toml | 1 + src/gwproto/decoders.py | 179 ++++++++-------- src/gwproto/message.py | 15 +- src/gwproto/messages/event.py | 13 +- src/gwproto/messages/misc.py | 12 +- src/gwproto/property_format.py | 9 +- .../types/component_attribute_class_gt.py | 12 +- src/gwproto/types/component_gt.py | 12 +- src/gwproto/types/data_channel.py | 12 +- src/gwproto/types/egauge_io.py | 4 +- src/gwproto/types/egauge_register_config.py | 4 +- src/gwproto/types/electric_meter_cac_gt.py | 9 +- .../types/electric_meter_component_gt.py | 19 +- .../types/fibaro_smart_implant_cac_gt.py | 8 +- .../fibaro_smart_implant_component_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 24 ++- .../types/gt_dispatch_boolean_local.py | 18 +- .../types/gt_driver_booleanactuator_cmd.py | 15 +- .../types/gt_sh_booleanactuator_cmd_status.py | 12 +- src/gwproto/types/gt_sh_cli_atn_cmd.py | 12 +- .../gt_sh_multipurpose_telemetry_status.py | 12 +- .../types/gt_sh_simple_telemetry_status.py | 12 +- src/gwproto/types/gt_sh_status.py | 21 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 18 +- src/gwproto/types/gt_telemetry.py | 9 +- src/gwproto/types/heartbeat_b.py | 28 ++- src/gwproto/types/hubitat_cac_gt.py | 2 +- src/gwproto/types/hubitat_component_gt.py | 2 +- src/gwproto/types/hubitat_gt.py | 7 +- src/gwproto/types/hubitat_poller_gt.py | 20 +- src/gwproto/types/hubitat_tank_cac_gt.py | 2 +- .../types/hubitat_tank_component_gt.py | 2 +- src/gwproto/types/hubitat_tank_gt.py | 38 ++-- .../types/multipurpose_sensor_cac_gt.py | 9 +- .../types/multipurpose_sensor_component_gt.py | 12 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 9 +- .../types/pipe_flow_sensor_component_gt.py | 12 +- src/gwproto/types/power_watts.py | 4 +- src/gwproto/types/relay_cac_gt.py | 9 +- src/gwproto/types/relay_component_gt.py | 12 +- src/gwproto/types/resistive_heater_cac_gt.py | 12 +- .../types/resistive_heater_component_gt.py | 12 +- src/gwproto/types/rest_poller_cac_gt.py | 2 +- src/gwproto/types/rest_poller_component_gt.py | 2 +- src/gwproto/types/rest_poller_gt.py | 61 ++---- .../types/simple_temp_sensor_cac_gt.py | 9 +- .../types/simple_temp_sensor_component_gt.py | 12 +- src/gwproto/types/snapshot_spaceheat.py | 12 +- src/gwproto/types/spaceheat_node_gt.py | 21 +- src/gwproto/types/ta_data_channels.py | 18 +- .../types/telemetry_reporting_config.py | 12 +- .../types/telemetry_snapshot_spaceheat.py | 12 +- src/gwproto/types/web_server_cac_gt.py | 2 +- src/gwproto/types/web_server_gt.py | 10 +- tests/decode_utils.py | 137 +++++++++++++ tests/test_debug.py | 9 - tests/test_decoders.py | 192 +++++++----------- tests/test_message.py | 27 +-- .../test_component_attribute_class_gt.py | 3 +- .../types/test_electric_meter_component_gt.py | 1 - ...t_sh_telemetry_from_multipurpose_sensor.py | 3 +- tests/types/test_heartbeat_b.py | 5 +- tests/types/test_hubitat_gt.py | 3 +- 63 files changed, 675 insertions(+), 534 deletions(-) create mode 100644 tests/decode_utils.py delete mode 100644 tests/test_debug.py diff --git a/pyproject.toml b/pyproject.toml index 3a59b3a5..d1bf2ab4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,6 +128,7 @@ ignore = [ "CPY", "D", "DOC", + "E302", "E501", "EM", "FA", # We only support Python >= 3.10, so we shouldn't need this diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index 6822d395..94c8b4b3 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -1,8 +1,11 @@ +# ruff: noqa: ANN401 + import abc import inspect import json import re import sys +import typing from abc import abstractmethod from dataclasses import dataclass from typing import ( @@ -19,7 +22,7 @@ ) import pydantic -from pydantic import BaseModel, Field, create_model +from pydantic import BaseModel, Field, ValidationError, create_model from gwproto.message import Header, Message from gwproto.messages import AnyEvent @@ -39,9 +42,9 @@ def decode_obj(self, o: Any) -> Any: ... class CallableDecoder(Decoder): _decode_obj: Callable[[Any], Any] - def __init__(self, decode_obj: Callable[[Any], Any]): + def __init__(self, decode_obj: Callable[[Any], Any]) -> None: if not callable(decode_obj): - raise ValueError( + raise TypeError( f"ERROR. decode_obj {decode_obj}/{type(decode_obj)} attribute is not Callable" ) self._decode_obj = decode_obj @@ -51,14 +54,14 @@ def decode_obj(self, o: Any) -> Any: class PydanticDecoder(CallableDecoder): - def __init__(self, model: Type["BaseModel"]): - super().__init__(model.parse_obj) + def __init__(self, model: Type["BaseModel"]) -> None: + super().__init__(model.model_validate) class MakerDecoder(CallableDecoder): DECODER_FUNCTION_NAME: str = "dict_to_tuple" - def __init__(self, model: Any): + def __init__(self, model: Any) -> None: decoder_function = getattr(model, self.DECODER_FUNCTION_NAME, None) if decoder_function is None: raise ValueError( @@ -78,50 +81,48 @@ def __init__( self, decoders: "Decoders", message_payload_discriminator: Optional[Type[MessageDiscriminator]] = None, - ): + ) -> None: self.decoders = decoders self.message_payload_discriminator = message_payload_discriminator + @classmethod + def _try_message_as_event(cls, message_dict: dict, e: ValidationError) -> Message: + special_typename_handling_message: Optional[Message[Any]] = None + for error in e.errors(): + if error.get("type", "") == "union_tag_invalid": + ctx = error.get("ctx", {}) + if ctx.get("discriminator", "") == "'TypeName'" and ctx.get( + "tag", "" + ).startswith("gridworks.event"): + try: + message_dict["Payload"] = AnyEvent(**message_dict["Payload"]) + special_typename_handling_message = Message(**message_dict) + except Exception as e2: # noqa: BLE001 + raise e2 from e + if special_typename_handling_message is None: + raise e + return special_typename_handling_message + def decode_obj(self, o: Any) -> Message[Any]: message_dict: dict = dict(o) - message_dict["Header"] = Header.parse_obj(message_dict.get("Header", dict())) + message_dict["Header"] = Header.model_validate(message_dict.get("Header", {})) message: Message[Any] if message_dict["Header"].MessageType in self.decoders: message_dict["Payload"] = self.decoders.decode_obj( message_dict["Header"].MessageType, - message_dict.get("Payload", dict()), + message_dict.get("Payload", {}), ) message = Message(**message_dict) else: try: - message = self.message_payload_discriminator.parse_obj(message_dict) + message = self.message_payload_discriminator.model_validate( + message_dict + ) except pydantic.ValidationError as e: # This can result because we receive a TypeName we don't recognize, either because the sender is # newer or older than our code. In some cases we can still meaningfully interpret the message, # for example if we can recognize that it is an event messasge, as is done here. - special_typename_handling_message: Optional[Message[Any]] = None - for error in e.errors(): - if ( - error.get("type", "") - == "value_error.discriminated_union.invalid_discriminator" - ): - ctx = error.get("ctx", dict()) - if ctx.get("discriminator_key", "") == "TypeName": - if ctx.get("discriminator_value", "").startswith( - "gridworks.event" - ): - try: - message_dict["Payload"] = AnyEvent( - **message_dict["Payload"] - ) - special_typename_handling_message = Message( - **message_dict - ) - except Exception as e2: - raise e2 from e - if special_typename_handling_message is None: - raise e - message = special_typename_handling_message + message = self._try_message_as_event(message_dict, e) return message @@ -130,7 +131,9 @@ class DecoderItem(NamedTuple): decoder: Decoder -DEFAULT_TYPE_NAME_FIELD = "TypeName" +DEFAULT_TYPE_NAME_FIELD: str = "TypeName" +DEFAULT_PAYLOAD_FIELD: str = "Payload" +DEFAULT_EXCLUDED_TYPE_NAMES: set[str] = {Message.type_name()} @dataclass @@ -139,8 +142,8 @@ class OneDecoderExtractor: decoder_function_name: str = "parse_obj" def get_type_name(self, obj: Any) -> str: - if not (type_name := getattr(obj, self.type_name_field, "")): - if (decoder_fields := getattr(obj, "__fields__", None)) is not None: + if not (type_name := getattr(obj, self.type_name_field, "")): # noqa: SIM102 + if (decoder_fields := getattr(obj, "model_fields", None)) is not None: # noqa: SIM102 if ( model_field := decoder_fields.get(self.type_name_field, None) ) is not None: @@ -157,7 +160,7 @@ def get_decoder(self, obj: Any) -> Optional[Decoder]: def extract(self, obj: Any) -> Optional[DecoderItem]: item = None - if type_name := self.get_type_name(obj): + if type_name := self.get_type_name(obj): # noqa: SIM102 if (decoder := self.get_decoder(obj)) is not None: item = DecoderItem(type_name, decoder) return item @@ -172,15 +175,17 @@ class MakerExtractor(OneDecoderExtractor): class Decoders: _decoders: dict[str, Decoder] - def __init__(self, decoders: Optional[dict[str, Decoder]] = None): - self._decoders = dict() + def __init__(self, decoders: Optional[dict[str, Decoder]] = None) -> None: + self._decoders = {} if decoders is not None: self.add_decoders(decoders) def decoder(self, type_name: str) -> Decoder: return self._decoders[type_name] - def decode_str(self, type_name: str, content: str | bytes, encoding="utf-8") -> Any: + def decode_str( + self, type_name: str, content: str | bytes, encoding: str = "utf-8" + ) -> Any: return self.decoder(type_name).decode_str(content, encoding=encoding) def decode_obj(self, type_name: str, o: Any) -> Any: @@ -198,7 +203,7 @@ def add_decoders(self, decoders: dict[str, Decoder]) -> "Decoders": return self def merge(self, other: "Decoders") -> "Decoders": - self.add_decoders(other._decoders) + self.add_decoders(other._decoders) # noqa: SLF001 return self def __contains__(self, type_name: str) -> bool: @@ -208,11 +213,10 @@ def types(self) -> list[str]: return list(self._decoders.keys()) def _validate(self, type_name: str, decoder: Decoder) -> None: - if type_name in self._decoders: - if self._decoders[type_name] is not decoder: - raise ValueError( - f"ERROR. decoder for [{type_name}] is already present as [{self._decoders[type_name]}]" - ) + if type_name in self._decoders and self._decoders[type_name] is not decoder: + raise ValueError( + f"ERROR. decoder for [{type_name}] is already present as [{self._decoders[type_name]}]" + ) @classmethod def from_objects( @@ -221,7 +225,7 @@ def from_objects( message_payload_discriminator: Optional[Type[MessageDiscriminator]] = None, extractors: Optional[Sequence[OneDecoderExtractor]] = None, ) -> "Decoders": - items = dict() + items = {} if objs is not None: if extractors is None: extractors = [OneDecoderExtractor(), MakerExtractor()] @@ -242,15 +246,15 @@ class MQTTCodec(abc.ABC): ENCODING = "utf-8" decoders: Decoders - def __init__(self, decoders: Decoders): + def __init__(self, decoders: Decoders) -> None: self.decoders = Decoders().merge(decoders) super().__init__() - def encode(self, content: Any) -> bytes: + def encode(self, content: bytes | BaseModel) -> bytes: if isinstance(content, bytes): encoded = content else: - encoded = content.json() + encoded = content.model_dump_json() if not isinstance(encoded, bytes): encoded = encoded.encode(self.ENCODING) return encoded @@ -258,7 +262,7 @@ def encode(self, content: Any) -> bytes: def decode(self, topic: str, payload: bytes) -> Any: decoded_topic = MQTTTopic.decode(topic) if decoded_topic.envelope_type not in self.decoders: - raise Exception( + raise ValueError( f"Type {decoded_topic.envelope_type} not recognized. Available decoders: {self.decoders.types()}" ) self.validate_source_alias(decoded_topic.src) @@ -267,25 +271,30 @@ def decode(self, topic: str, payload: bytes) -> Any: ) @abstractmethod - def validate_source_alias(self, source_alias: str): ... + def validate_source_alias(self, source_alias: str) -> None: ... def get_pydantic_literal_type_name( o: Any, type_name_field: str = DEFAULT_TYPE_NAME_FIELD ) -> str: - if hasattr(o, "__fields__"): - if type_name_field in o.__fields__: - if get_origin(o.__fields__[type_name_field].annotation) == Literal: - return str(o.__fields__[type_name_field].default) + if ( + hasattr(o, "model_fields") + and type_name_field in o.model_fields + and get_origin(o.model_fields[type_name_field].annotation) == Literal + ): + return str(o.model_fields[type_name_field].default) return "" -def pydantic_named_types( +def pydantic_named_types( # noqa: C901 module_names: str | Sequence[str], modules: Optional[Sequence[Any]] = None, type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, + excluded_type_names: Optional[set[str]] = None, ) -> list[Any]: + if excluded_type_names is None: + excluded_type_names = DEFAULT_EXCLUDED_TYPE_NAMES if isinstance(module_names, str): module_names = [module_names] if unimported := [ @@ -293,7 +302,7 @@ def pydantic_named_types( ]: raise ValueError(f"ERROR. modules {unimported} have not been imported.") named_types = [] - type_names: dict[str, Any] = dict() + accumulated_types: dict[str, Any] = {} if modules is None: modules = [] for module in [sys.modules[module_name] for module_name in module_names] + list( @@ -306,41 +315,44 @@ def pydantic_named_types( if type_name := get_pydantic_literal_type_name( module_class, type_name_field=type_name_field ): - if type_name in type_names: - if type_names[type_name] is module_class: + if type_name in excluded_type_names: + continue + if type_name in accumulated_types: + if accumulated_types[type_name] is module_class: continue raise ValueError( f"ERROR {type_name_field} ({type_name}) " f"for {module_class} already seen for " - f"class {type_names[type_name]}" + f"class {accumulated_types[type_name]}" ) - if type_name_regex is not None: - if not type_name_regex.match(type_name): - continue - type_names[type_name] = module_class + if type_name_regex is not None and not type_name_regex.match(type_name): + continue + accumulated_types[type_name] = module_class named_types.append(module_class) return named_types -def create_message_payload_discriminator( +def create_message_payload_discriminator( # noqa: PLR0913, PLR0917, RUF100 model_name: str, module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, + excluded_type_names: Optional[set[str]] = None, ) -> Type[MessageDiscriminator]: used_types = pydantic_named_types( module_names=module_names, modules=modules, type_name_field=type_name_field, type_name_regex=type_name_regex, + excluded_type_names=excluded_type_names, ) if explicit_types is not None: used_types.extend(explicit_types) return create_model( model_name, - __base__=Message, # type: ignore + __base__=Message, Payload=( Union[tuple(used_types)], Field(..., discriminator=type_name_field), @@ -348,20 +360,22 @@ def create_message_payload_discriminator( ) -def create_discriminator( +def create_discriminator( # noqa: PLR0913, PLR0917, RUF100 model_name: str, module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, - payload_field_name="Payload", + payload_field_name: str = DEFAULT_PAYLOAD_FIELD, + excluded_type_names: Optional[set[str]] = None, ) -> BaseModel: used_types = pydantic_named_types( module_names=module_names, modules=modules, type_name_field=type_name_field, type_name_regex=type_name_regex, + excluded_type_names=excluded_type_names, ) if explicit_types is not None: used_types.extend(explicit_types) @@ -375,8 +389,7 @@ def create_discriminator( Union[tuple(used_types)], Field(..., discriminator=type_name_field), ) - payload_field_kwargs = {payload_field_name: payload_field_type} - return create_model(model_name, **payload_field_kwargs) + return create_model(model_name, **{payload_field_name: payload_field_type}) class PydanticTypeNameDecoder(Decoder): @@ -384,7 +397,7 @@ class PydanticTypeNameDecoder(Decoder): payload_field_name: str contains: set[str] - def __init__( + def __init__( # noqa: PLR0913 self, model_name: str, *, @@ -393,8 +406,8 @@ def __init__( explicit_types: Optional[Sequence[Any]] = None, type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, - payload_field_name="Payload", - ): + payload_field_name: str = "Payload", + ) -> None: self.payload_field_name = payload_field_name self.loader = create_discriminator( model_name=model_name, @@ -405,17 +418,23 @@ def __init__( type_name_regex=type_name_regex, payload_field_name=payload_field_name, ) - payload_field = self.loader.__fields__[self.payload_field_name] - if payload_field.sub_fields_mapping is not None: - self.contains = set(payload_field.sub_fields_mapping.keys()) - else: - self.contains = {payload_field.type_.__fields__["TypeName"].default} + payload_field_annotation = self.loader.model_fields[ + self.payload_field_name + ].annotation + if payload_field_annotation is None: + raise ValueError( + f"ERROR. Payload field <{payload_field_name}> has no annotation" + ) + self.contains = { + payload_type.model_fields["TypeName"].default + for payload_type in typing.get_args(payload_field_annotation) + } def __contains__(self, type_name: str) -> bool: return type_name in self.contains def decode_obj(self, data: dict) -> BaseModel: return getattr( - self.loader.parse_obj({self.payload_field_name: data}), + self.loader.model_validate({self.payload_field_name: data}), self.payload_field_name, ) diff --git a/src/gwproto/message.py b/src/gwproto/message.py index 70b27514..62554d01 100644 --- a/src/gwproto/message.py +++ b/src/gwproto/message.py @@ -1,7 +1,8 @@ +# ruff: noqa: ANN401 + from typing import Any, Callable, Generic, Literal, Mapping, Optional, TypeVar, Union from pydantic import BaseModel -from pydantic.generics import GenericModel from gwproto.topic import MQTTTopic @@ -40,12 +41,12 @@ def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict) -> None: kwargs_dict[arg_name] = default_value -class Message(GenericModel, Generic[PayloadT]): +class Message(BaseModel, Generic[PayloadT]): Header: Header Payload: PayloadT - TypeName: Literal[GRIDWORKS_ENVELOPE_TYPE] = GRIDWORKS_ENVELOPE_TYPE + TypeName: Literal["gw"] = GRIDWORKS_ENVELOPE_TYPE - def __init__(self, header: Optional[Header] = None, **kwargs: Any): + def __init__(self, header: Optional[Header] = None, **kwargs: Any) -> None: if header is None: header = self._header_from_kwargs(kwargs) super().__init__(Header=header, **kwargs) @@ -58,14 +59,14 @@ def src(self) -> str: @classmethod def type_name(cls) -> str: - return Message.__fields__["TypeName"].default + return Message.model_fields["TypeName"].default def mqtt_topic(self) -> str: return MQTTTopic.encode(self.type_name(), self.src(), self.message_type()) @classmethod def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> Header: - header_kwargs = dict() + header_kwargs = {} if "Payload" in kwargs: payload = kwargs["Payload"] for header_field, payload_fields in [ @@ -86,7 +87,7 @@ def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> Header: header_kwargs[header_field] = val header: Optional[Union[Header, dict[str, Any]]] = kwargs.pop("Header", None) if isinstance(header, Header): - header = header.copy(update=header_kwargs, deep=True) + header = header.model_copy(update=header_kwargs, deep=True) else: if header is not None: header_kwargs = dict(header, **header_kwargs) diff --git a/src/gwproto/messages/event.py b/src/gwproto/messages/event.py index 1d8143fe..e901b711 100644 --- a/src/gwproto/messages/event.py +++ b/src/gwproto/messages/event.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any, Generic, Literal, Optional, TypeVar -from pydantic import BaseModel, Extra, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.message import Message, as_enum from gwproto.types import GtShStatus, SnapshotSpaceheat @@ -13,10 +13,10 @@ class EventBase(BaseModel): MessageId: str = Field(default_factory=lambda: str(uuid.uuid4())) TimeNS: int = Field(default_factory=time.time_ns) Src: str = "" - TypeName: str = Field(const=True) + TypeName: str -class AnyEvent(EventBase, extra=Extra.allow): +class AnyEvent(EventBase, extra="allow"): MessageId: str TimeNS: int Src: str @@ -53,7 +53,8 @@ class ProblemEvent(EventBase): Details: str = "" TypeName: Literal["gridworks.event.problem"] = "gridworks.event.problem" - @validator("ProblemType", pre=True) + @field_validator("ProblemType", mode="before") + @classmethod def problem_type_value(cls, v: Any) -> Optional[Problems]: return as_enum(v, Problems, Problems.error) @@ -102,12 +103,12 @@ class PeerActiveEvent(CommEvent): class GtShStatusEvent(EventBase): - status: GtShStatus | dict + status: GtShStatus TypeName: Literal["gridworks.event.gt.sh.status"] = "gridworks.event.gt.sh.status" class SnapshotSpaceheatEvent(EventBase): - snap: SnapshotSpaceheat | dict + snap: SnapshotSpaceheat TypeName: Literal["gridworks.event.snapshot.spaceheat"] = ( "gridworks.event.snapshot.spaceheat" ) diff --git a/src/gwproto/messages/misc.py b/src/gwproto/messages/misc.py index c75f951b..86eebc6c 100644 --- a/src/gwproto/messages/misc.py +++ b/src/gwproto/messages/misc.py @@ -1,3 +1,5 @@ +# ruff: noqa: ANN401 + import uuid from typing import Any, Literal @@ -17,9 +19,7 @@ class Ping(BaseModel): class PingMessage(Message[Ping]): - def __init__(self, **data: Any): - if "AckRequired" not in data: - data["AckRequired"] = True - if "Payload" not in data: - data["Payload"] = Ping() - super().__init__(**data) + def __init__(self, *, AckRequired: bool = True, **kwargs: Any) -> None: # noqa: N803 + if "Payload" not in kwargs: + kwargs["Payload"] = Ping() + super().__init__(AckRequired=AckRequired, **kwargs) diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index fada6e29..97b84868 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -7,8 +7,11 @@ def predicate_validator( - field_name: str, predicate: Callable[[Any], bool], error_format: str = "", **kwargs -) -> classmethod: # type: ignore + field_name: str, + predicate: Callable[[Any], bool], + error_format: str = "", + **kwargs: dict[str, Any], +) -> classmethod: """ Produce a pydantic validator from a function returning a bool. @@ -52,7 +55,7 @@ def _validator(v: Any) -> Any: raise ValueError(err_str) return v - return pydantic.validator(field_name, allow_reuse=True, **kwargs)(_validator) + return pydantic.field_validator(field_name, **kwargs)(_validator) def is_hex_char(v: str) -> bool: diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index af9f3bde..09499be7 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.errors import SchemaError @@ -45,11 +45,11 @@ class ComponentAttributeClassGt(BaseModel): ), default=None, ) - # TODO: bump-pydnatic hack; restore this to "component.attribute.class.gt" - TypeName: Literal["component.attribute.CLASS.gt"] = "component.attribute.CLASS.gt" + TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -77,8 +77,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"}, + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"}, by_alias=True, ).items() if value is not None diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index ef078dd0..a0ba3588 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.component import Component from gwproto.errors import SchemaError @@ -60,7 +60,8 @@ class ComponentGt(BaseModel): TypeName: Literal["component.gt"] = "component.gt" Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -70,7 +71,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -98,8 +100,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"}, + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"}, by_alias=True, ).items() if value is not None diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py index 98ea8de6..ef5af792 100644 --- a/src/gwproto/types/data_channel.py +++ b/src/gwproto/types/data_channel.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.errors import SchemaError @@ -50,7 +50,8 @@ class DataChannel(BaseModel): TypeName: Literal["data.channel"] = "data.channel" Version: Literal["000"] = "000" - @validator("AboutName") + @field_validator("AboutName") + @classmethod def _check_about_name(cls, v: str) -> str: try: check_is_spaceheat_name(v) @@ -58,7 +59,8 @@ def _check_about_name(cls, v: str) -> str: raise ValueError(f"AboutName failed SpaceheatName format validation: {e}") return v - @validator("CapturedByName") + @field_validator("CapturedByName") + @classmethod def _check_captured_by_name(cls, v: str) -> str: try: check_is_spaceheat_name(v) @@ -86,8 +88,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index b5cbe287..bf1df75d 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -70,8 +70,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index c7aabcc9..efa1c2ac 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -83,8 +83,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index a2e409ad..8c4700ee 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.enums import LocalCommInterface, TelemetryName @@ -69,7 +69,8 @@ class ElectricMeterCacGt(BaseModel): TypeName: Literal["electric.meter.cac.gt"] = "electric.meter.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -97,8 +98,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index ee3c78bc..4d7527f8 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Optional, Self -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, @@ -93,7 +93,8 @@ class ElectricMeterComponentGt(BaseModel): TypeName: Literal["electric.meter.component.gt"] = "electric.meter.component.gt" Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -103,7 +104,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -113,7 +115,8 @@ def _check_component_attribute_class_id(cls, v: str) -> str: ) return v - @validator("ModbusPort") + @field_validator("ModbusPort") + @classmethod def _check_modbus_port(cls, v: Optional[int]) -> Optional[int]: if v is None: return v @@ -129,10 +132,8 @@ def _check_modbus_port(cls, v: Optional[int]) -> Optional[int]: def check_axiom_1(self) -> Self: """ Axiom 1: Modbus consistency. - ModbusHost is None if and only if ModbusPort is None + ModbusHost requires a ModbusPort """ - if self.ModbusHost is None and self.ModbusPort is not None: - raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") if self.ModbusHost is not None and self.ModbusPort is None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") return self @@ -176,8 +177,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index 978ee80f..b7678477 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -2,7 +2,7 @@ import typing from typing import Any, Literal -from pydantic import Extra +from pydantic import ConfigDict from gwproto.data_classes.cacs.fibaro_smart_implant_cac import FibaroSmartImplantCac from gwproto.data_classes.component_attribute_class import ComponentAttributeClass @@ -13,9 +13,7 @@ class FibaroSmartImplantCacGt(ComponentAttributeClassGt): Model: str = "" TypeName: Literal["fibaro.smart.implant.cac.gt"] = "fibaro.smart.implant.cac.gt" Version: Literal["000"] = "000" - - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") @classmethod def from_data_class(cls, cac: FibaroSmartImplantCac) -> "FibaroSmartImplantCacGt": @@ -38,7 +36,7 @@ def __hash__(self): class FibaroSmartImplantCacGt_Maker: - type_name: str = FibaroSmartImplantCacGt.__fields__["TypeName"].default + type_name: str = FibaroSmartImplantCacGt.model_fields["TypeName"].default version = "000" tuple: FibaroSmartImplantCacGt diff --git a/src/gwproto/types/fibaro_smart_implant_component_gt.py b/src/gwproto/types/fibaro_smart_implant_component_gt.py index fd87d3fa..1771e40b 100644 --- a/src/gwproto/types/fibaro_smart_implant_component_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_component_gt.py @@ -43,7 +43,7 @@ def to_data_class(self) -> FibaroSmartImplantComponent: class FibaroSmartImplantComponentGt_Maker: - type_name: str = FibaroSmartImplantComponentGt.__fields__["TypeName"].default + type_name: str = FibaroSmartImplantComponentGt.model_fields["TypeName"].default version = "000" tuple: FibaroSmartImplantComponentGt diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 908b94c1..74cd34fd 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -52,7 +52,8 @@ class GtDispatchBoolean(BaseModel): TypeName: Literal["gt.dispatch.boolean"] = "gt.dispatch.boolean" Version: Literal["110"] = "110" - @validator("AboutNodeName") + @field_validator("AboutNodeName") + @classmethod def _check_about_node_name(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -62,7 +63,8 @@ def _check_about_node_name(cls, v: str) -> str: ) return v - @validator("ToGNodeAlias") + @field_validator("ToGNodeAlias") + @classmethod def _check_to_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -70,7 +72,8 @@ def _check_to_g_node_alias(cls, v: str) -> str: raise ValueError(f"ToGNodeAlias failed LeftRightDot format validation: {e}") return v - @validator("FromGNodeAlias") + @field_validator("FromGNodeAlias") + @classmethod def _check_from_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -80,7 +83,8 @@ def _check_from_g_node_alias(cls, v: str) -> str: ) return v - @validator("FromGNodeInstanceId") + @field_validator("FromGNodeInstanceId") + @classmethod def _check_from_g_node_instance_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -90,7 +94,8 @@ def _check_from_g_node_instance_id(cls, v: str) -> str: ) return v - @validator("RelayState", pre=True) + @field_validator("RelayState", mode="before") + @classmethod def _check_relay_state(cls, v: int) -> int: try: check_is_bit(v) @@ -98,7 +103,8 @@ def _check_relay_state(cls, v: int) -> int: raise ValueError(f"RelayState failed Bit format validation: {e}") return v - @validator("SendTimeUnixMs") + @field_validator("SendTimeUnixMs") + @classmethod def _check_send_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -126,8 +132,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 2ce64f06..945b5cda 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -47,7 +47,8 @@ class GtDispatchBooleanLocal(BaseModel): TypeName: Literal["gt.dispatch.boolean.local"] = "gt.dispatch.boolean.local" Version: Literal["110"] = "110" - @validator("RelayState", pre=True) + @field_validator("RelayState", mode="before") + @classmethod def _check_relay_state(cls, v: int) -> int: try: check_is_bit(v) @@ -55,7 +56,8 @@ def _check_relay_state(cls, v: int) -> int: raise ValueError(f"RelayState failed Bit format validation: {e}") return v - @validator("AboutNodeName") + @field_validator("AboutNodeName") + @classmethod def _check_about_node_name(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -65,7 +67,8 @@ def _check_about_node_name(cls, v: str) -> str: ) return v - @validator("FromNodeName") + @field_validator("FromNodeName") + @classmethod def _check_from_node_name(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -73,7 +76,8 @@ def _check_from_node_name(cls, v: str) -> str: raise ValueError(f"FromNodeName failed LeftRightDot format validation: {e}") return v - @validator("SendTimeUnixMs") + @field_validator("SendTimeUnixMs") + @classmethod def _check_send_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -101,8 +105,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 05cb308a..d445bac9 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -37,7 +37,8 @@ class GtDriverBooleanactuatorCmd(BaseModel): TypeName: Literal["gt.driver.booleanactuator.cmd"] = "gt.driver.booleanactuator.cmd" Version: Literal["100"] = "100" - @validator("RelayState", pre=True) + @field_validator("RelayState", mode="before") + @classmethod def _check_relay_state(cls, v: int) -> int: try: check_is_bit(v) @@ -45,7 +46,8 @@ def _check_relay_state(cls, v: int) -> int: raise ValueError(f"RelayState failed Bit format validation: {e}") return v - @validator("ShNodeAlias") + @field_validator("ShNodeAlias") + @classmethod def _check_sh_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -53,7 +55,8 @@ def _check_sh_node_alias(cls, v: str) -> str: raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") return v - @validator("CommandTimeUnixMs") + @field_validator("CommandTimeUnixMs") + @classmethod def _check_command_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -81,8 +84,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 4b367031..0f875e19 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -45,7 +45,8 @@ class GtShBooleanactuatorCmdStatus(BaseModel): ) Version: Literal["100"] = "100" - @validator("ShNodeAlias") + @field_validator("ShNodeAlias") + @classmethod def _check_sh_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -53,7 +54,8 @@ def _check_sh_node_alias(cls, v: str) -> str: raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") return v - @validator("CommandTimeUnixMsList") + @field_validator("CommandTimeUnixMsList") + @classmethod def _check_command_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: @@ -82,8 +84,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index 1d879634..5c9cbce0 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -41,7 +41,8 @@ class GtShCliAtnCmd(BaseModel): TypeName: Literal["gt.sh.cli.atn.cmd"] = "gt.sh.cli.atn.cmd" Version: Literal["110"] = "110" - @validator("FromGNodeAlias") + @field_validator("FromGNodeAlias") + @classmethod def _check_from_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -51,7 +52,8 @@ def _check_from_g_node_alias(cls, v: str) -> str: ) return v - @validator("FromGNodeId") + @field_validator("FromGNodeId") + @classmethod def _check_from_g_node_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -79,8 +81,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index c93f28ec..bc137a0e 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.errors import SchemaError @@ -64,7 +64,8 @@ class GtShMultipurposeTelemetryStatus(BaseModel): ) Version: Literal["100"] = "100" - @validator("AboutNodeAlias") + @field_validator("AboutNodeAlias") + @classmethod def _check_about_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -74,7 +75,8 @@ def _check_about_node_alias(cls, v: str) -> str: ) return v - @validator("ReadTimeUnixMsList") + @field_validator("ReadTimeUnixMsList") + @classmethod def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: @@ -115,8 +117,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 77cfecca..0e6c67aa 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.errors import SchemaError @@ -53,7 +53,8 @@ class GtShSimpleTelemetryStatus(BaseModel): TypeName: Literal["gt.sh.simple.telemetry.status"] = "gt.sh.simple.telemetry.status" Version: Literal["100"] = "100" - @validator("ShNodeAlias") + @field_validator("ShNodeAlias") + @classmethod def _check_sh_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -61,7 +62,8 @@ def _check_sh_node_alias(cls, v: str) -> str: raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") return v - @validator("ReadTimeUnixMsList") + @field_validator("ReadTimeUnixMsList") + @classmethod def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: @@ -102,8 +104,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index bda93ce6..a1067b15 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError from gwproto.types.gt_sh_booleanactuator_cmd_status import ( @@ -62,7 +62,8 @@ class GtShStatus(BaseModel): TypeName: Literal["gt.sh.status"] = "gt.sh.status" Version: Literal["110"] = "110" - @validator("FromGNodeAlias") + @field_validator("FromGNodeAlias") + @classmethod def _check_from_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -72,7 +73,8 @@ def _check_from_g_node_alias(cls, v: str) -> str: ) return v - @validator("FromGNodeId") + @field_validator("FromGNodeId") + @classmethod def _check_from_g_node_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -82,7 +84,8 @@ def _check_from_g_node_id(cls, v: str) -> str: ) return v - @validator("AboutGNodeAlias") + @field_validator("AboutGNodeAlias") + @classmethod def _check_about_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -92,7 +95,8 @@ def _check_about_g_node_alias(cls, v: str) -> str: ) return v - @validator("SlotStartUnixS") + @field_validator("SlotStartUnixS") + @classmethod def _check_slot_start_unix_s(cls, v: int) -> int: try: check_is_reasonable_unix_time_s(v) @@ -102,7 +106,8 @@ def _check_slot_start_unix_s(cls, v: int) -> int: ) return v - @validator("StatusUid") + @field_validator("StatusUid") + @classmethod def _check_status_uid(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -130,8 +135,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 260ed789..6ff055d7 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError @@ -45,12 +45,13 @@ class GtShTelemetryFromMultipurposeSensor(BaseModel): ValueList: List[int] = Field( title="ValueList", ) - TypeName: Literal["gt.sh.telemetry.FROM.multipurpose.sensor"] = ( - "gt.sh.telemetry.FROM.multipurpose.sensor" # TODO: bump-pydnatic hack; restore this to "gt.sh.telemetry.from.multipurpose.sensor" + TypeName: Literal["gt.sh.telemetry.from.multipurpose.sensor"] = ( + "gt.sh.telemetry.from.multipurpose.sensor" ) Version: Literal["100"] = "100" - @validator("ScadaReadTimeUnixMs") + @field_validator("ScadaReadTimeUnixMs") + @classmethod def _check_scada_read_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -60,7 +61,8 @@ def _check_scada_read_time_unix_ms(cls, v: int) -> int: ) return v - @validator("AboutNodeAliasList") + @field_validator("AboutNodeAliasList") + @classmethod def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: for elt in v: try: @@ -85,7 +87,7 @@ def check_axiom_1(self) -> Self: raise ValueError( "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." ) - return v + return self def as_dict(self) -> Dict[str, Any]: """ @@ -105,8 +107,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 52957a34..372bdafd 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError @@ -52,7 +52,8 @@ class GtTelemetry(BaseModel): TypeName: Literal["gt.telemetry"] = "gt.telemetry" Version: Literal["110"] = "110" - @validator("ScadaReadTimeUnixMs") + @field_validator("ScadaReadTimeUnixMs") + @classmethod def _check_scada_read_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -80,8 +81,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 60009a96..9402b9c3 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -53,9 +53,10 @@ class HeartbeatB(BaseModel): ), ) TypeName: Literal["heartbeat.b"] = "heartbeat.b" - Version: Literal["_001"] = "_001" # TODO: bump-pydnatic hack; restore this to "000" + Version: Literal["001"] = "001" - @validator("FromGNodeAlias") + @field_validator("FromGNodeAlias") + @classmethod def _check_from_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -65,7 +66,8 @@ def _check_from_g_node_alias(cls, v: str) -> str: ) return v - @validator("FromGNodeInstanceId") + @field_validator("FromGNodeInstanceId") + @classmethod def _check_from_g_node_instance_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -75,7 +77,8 @@ def _check_from_g_node_instance_id(cls, v: str) -> str: ) return v - @validator("MyHex") + @field_validator("MyHex") + @classmethod def _check_my_hex(cls, v: str) -> str: try: check_is_hex_char(v) @@ -83,7 +86,8 @@ def _check_my_hex(cls, v: str) -> str: raise ValueError(f"MyHex failed HexChar format validation: {e}") return v - @validator("YourLastHex") + @field_validator("YourLastHex") + @classmethod def _check_your_last_hex(cls, v: str) -> str: try: check_is_hex_char(v) @@ -91,7 +95,8 @@ def _check_your_last_hex(cls, v: str) -> str: raise ValueError(f"YourLastHex failed HexChar format validation: {e}") return v - @validator("LastReceivedTimeUnixMs") + @field_validator("LastReceivedTimeUnixMs") + @classmethod def _check_last_received_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -101,7 +106,8 @@ def _check_last_received_time_unix_ms(cls, v: int) -> int: ) return v - @validator("SendTimeUnixMs") + @field_validator("SendTimeUnixMs") + @classmethod def _check_send_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -129,8 +135,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } @@ -251,7 +257,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: raise SchemaError(f"TypeName missing from dict <{d2}>") if "Version" not in d2: raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "_001": # TODO: bump-pydnatic hack; restore this to "000" + if d2["Version"] != "001": LOGGER.debug( f"Attempting to interpret heartbeat.b version {d2['Version']} as version 001" ) diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index 3398051a..6e3d2395 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -32,7 +32,7 @@ def __hash__(self): class HubitatCacGt_Maker: - type_name: str = HubitatCacGt.__fields__["TypeName"].default + type_name: str = HubitatCacGt.model_fields["TypeName"].default version = "000" tuple: HubitatCacGt diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index 4fd3564e..3bbe42dc 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -99,7 +99,7 @@ def __init__(self, component_gt: HubitatComponentGt): class HubitatComponentGt_Maker: - type_name: str = HubitatComponentGt.__fields__["TypeName"].default + type_name: str = HubitatComponentGt.model_fields["TypeName"].default version = "000" tuple: HubitatComponentGt diff --git a/src/gwproto/types/hubitat_gt.py b/src/gwproto/types/hubitat_gt.py index 94276475..019422c7 100644 --- a/src/gwproto/types/hubitat_gt.py +++ b/src/gwproto/types/hubitat_gt.py @@ -1,7 +1,7 @@ from typing import Optional import yarl -from pydantic import BaseModel, Extra +from pydantic import BaseModel, ConfigDict from gwproto.property_format import predicate_validator from gwproto.types.rest_poller_gt import URLArgs, URLConfig @@ -14,10 +14,7 @@ class HubitatGt(BaseModel): AccessToken: str MacAddress: str WebListenEnabled: bool = True - - class Config: - extra = Extra.allow - allow_population_by_field_name = True + model_config = ConfigDict(extra="allow", populate_by_name=True) _is_mac_address = predicate_validator("MacAddress", has_mac_address_format) diff --git a/src/gwproto/types/hubitat_poller_gt.py b/src/gwproto/types/hubitat_poller_gt.py index 580b18ce..eca5b186 100644 --- a/src/gwproto/types/hubitat_poller_gt.py +++ b/src/gwproto/types/hubitat_poller_gt.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Extra, validator +from pydantic import BaseModel, ConfigDict, field_validator from gwproto.enums import TelemetryName, Unit from gwproto.utils import snake_to_camel @@ -31,12 +31,12 @@ def unit(self) -> Unit: ) return Unit(value) - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) - @validator("telemetry_name_gt_enum_symbol") + @field_validator("telemetry_name_gt_enum_symbol") + @classmethod def _check_telemetry_name_symbol(cls, v: str) -> str: if v not in TelemetryName.symbols(): v = TelemetryName.value_to_symbol(TelemetryName.default()) @@ -50,8 +50,6 @@ class HubitatPollerGt(BaseModel): enabled: bool = True web_listen_enabled: bool = True poll_period_seconds: float = 60 - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index baf6598f..446c9f81 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -32,7 +32,7 @@ def __hash__(self): class HubitatTankCacGt_Maker: - type_name: str = HubitatTankCacGt.__fields__["TypeName"].default + type_name: str = HubitatTankCacGt.model_fields["TypeName"].default version = "000" tuple: HubitatTankCacGt diff --git a/src/gwproto/types/hubitat_tank_component_gt.py b/src/gwproto/types/hubitat_tank_component_gt.py index 44d95c7d..1ba63732 100644 --- a/src/gwproto/types/hubitat_tank_component_gt.py +++ b/src/gwproto/types/hubitat_tank_component_gt.py @@ -46,7 +46,7 @@ def to_data_class(self) -> HubitatTankComponent: class HubitatTankComponentGt_Maker: - type_name: str = HubitatTankComponentGt.__fields__["TypeName"].default + type_name: str = HubitatTankComponentGt.model_fields["TypeName"].default version = "000" tuple: HubitatTankComponentGt diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index 3126f973..d8796cef 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -4,7 +4,8 @@ from typing import Optional import yarl -from pydantic import BaseModel, Extra, conint, validator +from pydantic import BaseModel, ConfigDict, Field, field_validator +from typing_extensions import Annotated from gwproto.enums import TelemetryName, Unit from gwproto.types.hubitat_component_gt import HubitatRESTResolutionSettings @@ -26,10 +27,10 @@ class FibaroTempSensorSettingsGt(BaseModel): - stack_depth: conint(ge=1) + stack_depth: Annotated[int, Field(ge=1)] device_id: int fibaro_component_id: str - analog_input_id: conint(ge=1, le=2) + analog_input_id: Annotated[int, Field(ge=1, le=2)] tank_label: str = "" exponent: int = 1 telemetry_name_gt_enum_symbol: str = "c89d0ba1" @@ -44,19 +45,19 @@ class FibaroTempSensorSettingsGt(BaseModel): 4. The default value. """ rest: Optional[RESTPollerSettings] = None + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True - - @validator("telemetry_name_gt_enum_symbol") + @field_validator("telemetry_name_gt_enum_symbol") + @classmethod def _check_telemetry_name_symbol(cls, v: str) -> str: if v not in TelemetryName.symbols(): v = TelemetryName.value_to_symbol(TelemetryName.default()) return v - @validator("temp_unit_gt_enum_symbol") + @field_validator("temp_unit_gt_enum_symbol") + @classmethod def _checktemp_unit_gt_enum_symbol(cls, v: str) -> str: if v not in Unit.symbols(): v = Unit.value_to_symbol(Unit.default()) @@ -68,11 +69,10 @@ def _checktemp_unit_gt_enum_symbol(cls, v: str) -> str: class FibaroTempSensorSettings(FibaroTempSensorSettingsGt): node_name: str + model_config = ConfigDict(ignored_types=(cached_property, TelemetryName)) - class Config: - keep_untouched = (cached_property, TelemetryName) - - @validator("rest") + @field_validator("rest") + @classmethod def _collapse_rest_url(cls, v: Optional[RESTPollerSettings]): if v is not None: # Collapse session.base_url and request.url into @@ -216,7 +216,7 @@ def create( tank_name=tank_name, stack_depth=settings_gt.stack_depth, ), - **settings_gt.dict(), + **settings_gt.model_dump(), ) if settings.poll_period_seconds is None: settings.poll_period_seconds = default_poll_period_seconds @@ -233,8 +233,6 @@ class HubitatTankSettingsGt(BaseModel): default_poll_period_seconds: Optional[float] = None devices: list[FibaroTempSensorSettingsGt] = [] web_listen_enabled: bool = True - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 11e23e47..7f7416f2 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac from gwproto.enums import MakeModel as EnumMakeModel @@ -82,7 +82,8 @@ class MultipurposeSensorCacGt(BaseModel): TypeName: Literal["multipurpose.sensor.cac.gt"] = "multipurpose.sensor.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -110,8 +111,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index add78964..0575c756 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.components.multipurpose_sensor_component import ( MultipurposeSensorComponent, @@ -73,7 +73,8 @@ class MultipurposeSensorComponentGt(BaseModel): ) Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -83,7 +84,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -111,8 +113,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 2ae0610d..6a69c41d 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac from gwproto.enums import MakeModel as EnumMakeModel @@ -49,7 +49,8 @@ class PipeFlowSensorCacGt(BaseModel): TypeName: Literal["pipe.flow.sensor.cac.gt"] = "pipe.flow.sensor.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -77,8 +78,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index ac707e03..caec0307 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.components.pipe_flow_sensor_component import ( PipeFlowSensorComponent, @@ -71,7 +71,8 @@ class PipeFlowSensorComponentGt(BaseModel): TypeName: Literal["pipe.flow.sensor.component.gt"] = "pipe.flow.sensor.component.gt" Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -81,7 +82,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -109,8 +111,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 3c1f39dc..52fc92f5 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -50,8 +50,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 5bfc2906..143f34e5 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.relay_cac import RelayCac from gwproto.enums import MakeModel as EnumMakeModel @@ -47,7 +47,8 @@ class RelayCacGt(BaseModel): TypeName: Literal["relay.cac.gt"] = "relay.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -75,8 +76,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 8130b6c8..4c956515 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.components.relay_component import RelayComponent from gwproto.errors import SchemaError @@ -71,7 +71,8 @@ class RelayComponentGt(BaseModel): TypeName: Literal["relay.component.gt"] = "relay.component.gt" Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -81,7 +82,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -109,8 +111,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 1cac5011..6bc37c67 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac from gwproto.enums import MakeModel as EnumMakeModel @@ -50,7 +50,8 @@ class ResistiveHeaterCacGt(BaseModel): TypeName: Literal["resistive.heater.cac.gt"] = "resistive.heater.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -60,7 +61,8 @@ def _check_component_attribute_class_id(cls, v: str) -> str: ) return v - @validator("RatedVoltageV") + @field_validator("RatedVoltageV") + @classmethod def _check_rated_voltage_v(cls, v: int) -> int: try: check_is_positive_integer(v) @@ -88,8 +90,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 525acc3c..2bfc619d 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterComponent, @@ -68,7 +68,8 @@ class ResistiveHeaterComponentGt(BaseModel): TypeName: Literal["resistive.heater.component.gt"] = "resistive.heater.component.gt" Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -78,7 +79,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -106,8 +108,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index 2ed39541..ac7d838c 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -32,7 +32,7 @@ def __hash__(self): class RESTPollerCacGt_Maker: - type_name: str = RESTPollerCacGt.__fields__["TypeName"].default + type_name: str = RESTPollerCacGt.model_fields["TypeName"].default version = "000" tuple: RESTPollerCacGt diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index d3becc9a..0f69626c 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -46,7 +46,7 @@ def to_data_class(self) -> RESTPollerComponent: class RESTPollerComponentGt_Maker: - type_name: str = RESTPollerComponentGt.__fields__["TypeName"].default + type_name: str = RESTPollerComponentGt.model_fields["TypeName"].default version = "000" tuple: RESTPollerComponentGt diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index b29984b8..01105f0e 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -7,7 +7,7 @@ from typing import Literal, Optional, Self, Tuple import yarl -from pydantic import BaseModel, Extra, HttpUrl, model_validator +from pydantic import BaseModel, ConfigDict, HttpUrl, model_validator from gwproto.utils import snake_to_camel @@ -24,10 +24,7 @@ class URLArgs(BaseModel): query: Optional[list[Tuple[str, str | int | float]]] = None fragment: str = "" encoded: bool = True - - class Config: - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) @classmethod def dict_from_url(cls, url: str | yarl.URL) -> dict: @@ -81,10 +78,7 @@ class URLConfig(BaseModel): produce the URL 'path' field. The formatting operation is done as url_path_format.format(**URLPathArgs). See make_url() for details. """ - - class Config: - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) def to_url(self) -> yarl.URL: return self.make_url(self) @@ -126,20 +120,15 @@ class AioHttpClientTimeout(BaseModel): connect: Optional[float] = None sock_read: Optional[float] = None sock_connect: Optional[float] = None - - class Config: - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) class SessionArgs(BaseModel): base_url: Optional[URLConfig] = None timeout: Optional[AioHttpClientTimeout] = None - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) class RequestArgs(BaseModel): @@ -150,32 +139,26 @@ class RequestArgs(BaseModel): headers: Optional[dict] = None timeout: AioHttpClientTimeout = None ssl: Optional[bool] = None - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) class ErrorResponse(BaseModel): error_for_http_status: bool = True raise_exception: bool = False report: bool = True - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) class ErrorResponses(BaseModel): request: ErrorResponse = ErrorResponse() convert: ErrorResponse = ErrorResponse() - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) DEFAULT_REST_POLL_PERIOD_SECONDS = 60.0 @@ -186,12 +169,12 @@ class RESTPollerSettings(BaseModel): request: RequestArgs = RequestArgs() poll_period_seconds: float = DEFAULT_REST_POLL_PERIOD_SECONDS errors: ErrorResponses = ErrorResponses() - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True - keep_untouched = (cached_property,) + model_config = ConfigDict( + extra="allow", + alias_generator=snake_to_camel, + populate_by_name=True, + ignored_types=(cached_property,), + ) def url_args(self) -> dict: session_args = URLConfig.make_url_args(self.session.base_url) diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 76fb8f70..8d39582e 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac from gwproto.enums import MakeModel as EnumMakeModel @@ -67,7 +67,8 @@ class SimpleTempSensorCacGt(BaseModel): TypeName: Literal["simple.temp.sensor.cac.gt"] = "simple.temp.sensor.cac.gt" Version: Literal["000"] = "000" - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -95,8 +96,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index dbce2e8d..b702a558 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.components.simple_temp_sensor_component import ( SimpleTempSensorComponent, @@ -66,7 +66,8 @@ class SimpleTempSensorComponentGt(BaseModel): ) Version: Literal["000"] = "000" - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -76,7 +77,8 @@ def _check_component_id(cls, v: str) -> str: ) return v - @validator("ComponentAttributeClassId") + @field_validator("ComponentAttributeClassId") + @classmethod def _check_component_attribute_class_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -104,8 +106,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index a13ac7ed..0c425633 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError from gwproto.types.telemetry_snapshot_spaceheat import ( @@ -34,7 +34,8 @@ class SnapshotSpaceheat(BaseModel): TypeName: Literal["snapshot.spaceheat"] = "snapshot.spaceheat" Version: Literal["000"] = "000" - @validator("FromGNodeAlias") + @field_validator("FromGNodeAlias") + @classmethod def _check_from_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -44,7 +45,8 @@ def _check_from_g_node_alias(cls, v: str) -> str: ) return v - @validator("FromGNodeInstanceId") + @field_validator("FromGNodeInstanceId") + @classmethod def _check_from_g_node_instance_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -72,8 +74,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 2153e39c..c5967bd2 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.data_classes.sh_node import ShNode from gwproto.enums import ActorClass as EnumActorClass @@ -71,7 +71,8 @@ class SpaceheatNodeGt(BaseModel): TypeName: Literal["spaceheat.node.gt"] = "spaceheat.node.gt" Version: Literal["100"] = "100" - @validator("ShNodeId") + @field_validator("ShNodeId") + @classmethod def _check_sh_node_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -81,7 +82,8 @@ def _check_sh_node_id(cls, v: str) -> str: ) return v - @validator("Alias") + @field_validator("Alias") + @classmethod def _check_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -89,7 +91,8 @@ def _check_alias(cls, v: str) -> str: raise ValueError(f"Alias failed LeftRightDot format validation: {e}") return v - @validator("ComponentId") + @field_validator("ComponentId") + @classmethod def _check_component_id(cls, v: Optional[str]) -> Optional[str]: if v is None: return v @@ -101,7 +104,8 @@ def _check_component_id(cls, v: Optional[str]) -> Optional[str]: ) return v - @validator("RatedVoltageV") + @field_validator("RatedVoltageV") + @classmethod def _check_rated_voltage_v(cls, v: Optional[int]) -> Optional[int]: if v is None: return v @@ -113,7 +117,8 @@ def _check_rated_voltage_v(cls, v: Optional[int]) -> Optional[int]: ) return v - @validator("TypicalVoltageV") + @field_validator("TypicalVoltageV") + @classmethod def _check_typical_voltage_v(cls, v: Optional[int]) -> Optional[int]: if v is None: return v @@ -143,8 +148,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 9b0c80f4..3a0c8d12 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError from gwproto.types.data_channel import DataChannel, DataChannel_Maker @@ -54,7 +54,8 @@ class TaDataChannels(BaseModel): TypeName: Literal["ta.data.channels"] = "ta.data.channels" Version: Literal["000"] = "000" - @validator("TerminalAssetGNodeAlias") + @field_validator("TerminalAssetGNodeAlias") + @classmethod def _check_terminal_asset_g_node_alias(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -64,7 +65,8 @@ def _check_terminal_asset_g_node_alias(cls, v: str) -> str: ) return v - @validator("TerminalAssetGNodeId") + @field_validator("TerminalAssetGNodeId") + @classmethod def _check_terminal_asset_g_node_id(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -74,7 +76,8 @@ def _check_terminal_asset_g_node_id(cls, v: str) -> str: ) return v - @validator("TimeUnixS") + @field_validator("TimeUnixS") + @classmethod def _check_time_unix_s(cls, v: int) -> int: try: check_is_reasonable_unix_time_s(v) @@ -84,7 +87,8 @@ def _check_time_unix_s(cls, v: int) -> int: ) return v - @validator("Identifier") + @field_validator("Identifier") + @classmethod def _check_identifier(cls, v: str) -> str: try: check_is_uuid_canonical_textual(v) @@ -112,8 +116,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 312b3e8d..86f2f933 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Literal, Optional, Self -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName from gwproto.enums import Unit as EnumUnit @@ -55,7 +55,8 @@ class TelemetryReportingConfig(BaseModel): TypeName: Literal["telemetry.reporting.config"] = "telemetry.reporting.config" Version: Literal["000"] = "000" - @validator("AboutNodeName") + @field_validator("AboutNodeName") + @classmethod def _check_about_node_name(cls, v: str) -> str: try: check_is_left_right_dot(v) @@ -65,7 +66,8 @@ def _check_about_node_name(cls, v: str) -> str: ) return v - @validator("NameplateMaxValue") + @field_validator("NameplateMaxValue") + @classmethod def _check_nameplate_max_value(cls, v: Optional[int]) -> Optional[int]: if v is None: return v @@ -107,8 +109,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index f2234338..09c3238b 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self -from pydantic import BaseModel, Field, model_validator, validator +from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName from gwproto.errors import SchemaError @@ -51,7 +51,8 @@ class TelemetrySnapshotSpaceheat(BaseModel): TypeName: Literal["telemetry.snapshot.spaceheat"] = "telemetry.snapshot.spaceheat" Version: Literal["000"] = "000" - @validator("ReportTimeUnixMs") + @field_validator("ReportTimeUnixMs") + @classmethod def _check_report_time_unix_ms(cls, v: int) -> int: try: check_is_reasonable_unix_time_ms(v) @@ -61,7 +62,8 @@ def _check_report_time_unix_ms(cls, v: int) -> int: ) return v - @validator("AboutNodeAliasList") + @field_validator("AboutNodeAliasList") + @classmethod def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: for elt in v: try: @@ -106,8 +108,8 @@ def as_dict(self) -> Dict[str, Any]: """ d = { key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index 3ce1134b..3c2925d3 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -7,7 +7,7 @@ class WebServerCacGt(ComponentAttributeClassGt): - TypeName: Literal["hubitat.cac.gt"] = "web.server.cac.gt" + TypeName: Literal["web.server.cac.gt"] = "web.server.cac.gt" Version: Literal["000"] = "000" @classmethod diff --git a/src/gwproto/types/web_server_gt.py b/src/gwproto/types/web_server_gt.py index 62238ee0..6f3944e3 100644 --- a/src/gwproto/types/web_server_gt.py +++ b/src/gwproto/types/web_server_gt.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Extra +from pydantic import BaseModel, ConfigDict from gwproto.utils import snake_to_camel @@ -11,8 +11,6 @@ class WebServerGt(BaseModel): Port: int = 8080 Enabled: bool = True Kwargs: dict = {} - - class Config: - extra = Extra.allow - alias_generator = snake_to_camel - allow_population_by_field_name = True + model_config = ConfigDict( + extra="allow", alias_generator=snake_to_camel, populate_by_name=True + ) diff --git a/tests/decode_utils.py b/tests/decode_utils.py new file mode 100644 index 00000000..d6e69e79 --- /dev/null +++ b/tests/decode_utils.py @@ -0,0 +1,137 @@ +from dataclasses import dataclass, field +from typing import Any, Optional, Type + +from gwproto import Message, MQTTCodec + + +@dataclass +class MessageCase: + tag: str + src_message: Message + exp_message: Optional[Message] = None + exp_payload: Any = None + exp_exceptions: list[Type[Exception]] = field(default_factory=list) + + +@dataclass +class CaseError: + case_idx: int + case: MessageCase + + def __str__(self) -> str: + return f"{self.case.tag:30s} {self.case_idx:2d} {type(self)}" + + +@dataclass +class DecodeError(CaseError): + exception: Exception + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\t{type(self.exception)}" + f"\n\t\t{self.exception}" + ) + + +@dataclass +class MessageMatchError(CaseError): + exp_message: Message + decoded_message: Message + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\texp: {type(self.exp_message)}" + f"\n\t\tgot: {type(self.decoded_message)}" + ) + + +@dataclass +class PayloadMatchError(CaseError): + exp_payload: Any + decoded_payload: Any + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\texp: {type(self.exp_payload)}" + f"\n\t\tgot: {type(self.decoded_payload)}" + ) + + +@dataclass +class DecodeResult: + ok: bool + decoded: Message | None + exception: Exception | None + + +def _decode( + src_codec: MQTTCodec, + dst_codec: MQTTCodec, + case: MessageCase, +) -> DecodeResult: + decoded: Message | None + exception: Exception | None + ok: bool + try: + decoded = dst_codec.decode( + case.src_message.mqtt_topic(), + src_codec.encode(case.src_message), + ) + exception = None + except Exception as e: # noqa: BLE001 + decoded = None + exception = e + if decoded is None: + ok = type(exception) in case.exp_exceptions + else: + ok = not case.exp_exceptions + return DecodeResult(ok, decoded, exception) + + +def assert_encode_decode( + src_codec: MQTTCodec, + dst_codec: MQTTCodec, + messages: list[MessageCase], +) -> None: + errors: list[CaseError] = [] + for case_idx, case in enumerate(messages): + decode_result = _decode( + src_codec, + dst_codec, + case, + ) + if not decode_result.ok: + errors.append(DecodeError(case_idx, case, decode_result.exception)) + elif not case.exp_exceptions: + if case.exp_message is not None: + if decode_result.decoded != case.exp_message: + errors.append( + MessageMatchError( + case_idx, + case, + exp_message=case.exp_message, + decoded_message=decode_result.decoded, + ) + ) + else: + if case.exp_payload is None: + exp_payload = case.src_message.Payload + else: + exp_payload = case.exp_payload + if decode_result.decoded.Payload != exp_payload: + errors.append( + PayloadMatchError( + case_idx, + case, + exp_payload=exp_payload, + decoded_payload=decode_result.decoded.Payload, + ) + ) + if errors: + err_str = "ERROR. Got codec matching errors:" + for error in errors: + err_str += f"\n\t{error}" + raise ValueError(err_str) diff --git a/tests/test_debug.py b/tests/test_debug.py deleted file mode 100644 index bf48ed7d..00000000 --- a/tests/test_debug.py +++ /dev/null @@ -1,9 +0,0 @@ -from tests.dummy_decoders.parent.codec import ParentMQTTCodec - -dst_codec = ParentMQTTCodec() - -encoded_m = b'{"Header": {"Src": "child", "Dst": "", "MessageType": "gt.sh.status", "MessageId": "", "AckRequired": false, "TypeName": "gridworks.header"}, "Payload": {"AboutGNodeAlias": "hw1.isone.ct.newhaven.orange1.ta", "BooleanactuatorCmdList": [], "FromGNodeAlias": "hw1.isone.ct.newhaven.orange1.ta.scada", "FromGNodeId": "28817671-3899-4e24-a337-abcb8633e47a", "MultipurposeTelemetryList": [{"AboutNodeAlias": "a.elt1", "ReadTimeUnixMsList": [1676592055586], "SensorNodeAlias": "a.m", "TelemetryNameGtEnumSymbol": "af39eec9", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [0]}, {"AboutNodeAlias": "a.tank.out.temp2", "ReadTimeUnixMsList": [1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089], "SensorNodeAlias": "a.s.analog.temp", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [77, 361, -115, -9, -44]}, {"AboutNodeAlias": "a.tank.in.temp2", "ReadTimeUnixMsList": [1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089], "SensorNodeAlias": "a.s.analog.temp", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [-3587, -3574, -3771, -3656, -3626]}, {"AboutNodeAlias": "a.garage.temp2", "ReadTimeUnixMsList": [1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089], "SensorNodeAlias": "a.s.analog.temp", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [-3222, -3036, -3264, -3087, -3015]}, {"AboutNodeAlias": "a.tankoutside.temp2", "ReadTimeUnixMsList": [1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089], "SensorNodeAlias": "a.s.analog.temp", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [3011, 3037, 2893, 3037, 3041]}], "ReportingPeriodS": 300, "SimpleTelemetryList": [{"ReadTimeUnixMsList": [1676592052038], "ShNodeAlias": "a.elt1.relay", "TelemetryNameGtEnumSymbol": "5a71d4b3", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0]}, {"ReadTimeUnixMsList": [1676592232171], "ShNodeAlias": "a.tank.out.pump.relay", "TelemetryNameGtEnumSymbol": "5a71d4b3", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0]}, {"ReadTimeUnixMsList": [1676592079160], "ShNodeAlias": "a.tank.out.pump.baseboard1.fan.relay", "TelemetryNameGtEnumSymbol": "5a71d4b3", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0]}, {"ReadTimeUnixMsList": [1676592033601, 1676592094057, 1676592154524, 1676592215130, 1676592275685], "ShNodeAlias": "a.tank.out.temp1", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [18000, 17937, 17937, 17875, 17812]}, {"ReadTimeUnixMsList": [1676592044721, 1676592104326, 1676592164389, 1676592224395, 1676592284085], "ShNodeAlias": "a.tank.out.far.temp1", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [15375, 15312, 15312, 15312, 15250]}, {"ReadTimeUnixMsList": [1676592034321, 1676592095091, 1676592155316, 1676592215161, 1676592275192], "ShNodeAlias": "a.tank.in.temp1", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [14437, 14375, 14312, 14312, 14312]}, {"ReadTimeUnixMsList": [1676592035601, 1676592095132, 1676592155706, 1676592215733, 1676592275380], "ShNodeAlias": "a.tank.temp0", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [21812, 21812, 21812, 21812, 21812]}, {"ReadTimeUnixMsList": [1676592024051, 1676592084160, 1676592144739, 1676592204239, 1676592264435], "ShNodeAlias": "a.garage.temp1", "TelemetryNameGtEnumSymbol": "c89d0ba1", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [15125, 15062, 15062, 15062, 15062]}], "SlotStartUnixS": 1676592000, "StatusUid": "82abba64-d0df-4907-9b2b-6cb585421068", "TypeName": "gt.sh.status", "Version": "110"}, "TypeName": "gw"}' -decoded_m = dst_codec.decode( - "gw/child/gt-sh-status", - encoded_m, -) diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 25eb1471..7eb8151d 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -1,21 +1,12 @@ import json import time import uuid -from dataclasses import dataclass, field from pathlib import Path -from typing import Any, Optional, Type -from gwproto.messages import AnyEvent -from pydantic import ValidationError - -try: - from rich import print -except ImportError: - pass - -from gwproto import Message, MQTTCodec +from gwproto import Message from gwproto.messages import ( Ack, + AnyEvent, GtDispatchBoolean_Maker, GtShCliAtnCmd_Maker, GtShStatus_Maker, @@ -35,17 +26,19 @@ SnapshotSpaceheatEvent, StartupEvent, ) +from pydantic import ValidationError from tests.dummy_decoders import CHILD, PARENT from tests.dummy_decoders.child.codec import ChildMQTTCodec from tests.dummy_decoders.parent.codec import ParentMQTTCodec +from .decode_utils import MessageCase, assert_encode_decode + TEST_DATA_DIR = Path(__file__).parent / "data" def get_stored_message_dicts() -> dict: - d = {} - return d + return {} def child_to_parent_payload_dicts() -> dict: @@ -57,31 +50,23 @@ def child_to_parent_payload_dicts() -> dict: return d -@dataclass -class MessageCase: - src_message: Message - exp_message: Optional[Message] = None - exp_payload: Any = None - exp_exceptions: list[Type[Exception]] = field(default_factory=list) - - def child_to_parent_messages() -> list[MessageCase]: stored_message_dicts = child_to_parent_payload_dicts() status_message_dict = stored_message_dicts["status"] gt_sh_status = GtShStatus_Maker.dict_to_tuple(status_message_dict["Payload"]) gt_sh_status_event = GtShStatusEvent(Src=CHILD, status=gt_sh_status) - unrecognized_status_event = AnyEvent(**gt_sh_status_event.dict()) + unrecognized_status_event = AnyEvent(**gt_sh_status_event.model_dump()) unrecognized_status_event.TypeName += ".foo" unrecognized_event = AnyEvent( TypeName="gridworks.event.bar", MessageId="1", TimeNS=1, Src="1" ) unrecognizeable_not_event_type = AnyEvent( **dict( - gt_sh_status_event.dict(), + gt_sh_status_event.model_dump(), TypeName="bla", ) ) - unrecognizeable_bad_event_content = dict(TypeName="gridworks.event.baz") + unrecognizeable_bad_event_content = {"TypeName": "gridworks.event.baz"} snap_message_dict = stored_message_dicts["snapshot"] snapshot_spaceheat = SnapshotSpaceheat_Maker.dict_to_tuple( snap_message_dict["Payload"] @@ -91,42 +76,56 @@ def child_to_parent_messages() -> list[MessageCase]: return [ # Gs Pwr MessageCase( + "GsPwr", Message( Src=CHILD, MessageType="power.watts", Payload=PowerWatts_Maker(1).tuple - ) + ), ), # status # QUESTION: why does this fail when replacing "gt.sh.status.110" with "gt.sh.status"? MessageCase( + "status-payload-obj", Message(Src=CHILD, MessageType="gt.sh.status.110", Payload=gt_sh_status), None, gt_sh_status, ), MessageCase( + "status-payload-dict", Message(Src=CHILD, Payload=status_message_dict["Payload"]), None, gt_sh_status, ), MessageCase( - Message(Src=CHILD, Payload=gt_sh_status.as_dict()), None, gt_sh_status + "status-payload-as_dict", + Message(Src=CHILD, Payload=gt_sh_status.as_dict()), + None, + gt_sh_status, ), # snapshot - MessageCase(Message(**snap_message_dict), None, snapshot_spaceheat), + MessageCase("snap", Message(**snap_message_dict), None, snapshot_spaceheat), MessageCase( + "snap-payload-dict", Message(Src=CHILD, Payload=snap_message_dict["Payload"]), None, snapshot_spaceheat, ), MessageCase( + "snap-payload-as_dict", Message(Src=CHILD, Payload=snapshot_spaceheat.as_dict()), None, snapshot_spaceheat, ), # events - MessageCase(Message(Src=CHILD, Payload=gt_sh_status_event)), - MessageCase(Message(Src=CHILD, Payload=unrecognized_status_event)), - MessageCase(Message(Src=CHILD, Payload=unrecognized_event)), + MessageCase("event-status", Message(Src=CHILD, Payload=gt_sh_status_event)), + MessageCase( + "event-unrecognized-status", + Message(Src=CHILD, Payload=unrecognized_status_event), + ), MessageCase( + "event-unrecognized", Message(Src=CHILD, Payload=unrecognized_event) + ), + MessageCase( + "unrecognized-not-event", Message( Src=CHILD, Payload=unrecognizeable_not_event_type, @@ -134,34 +133,52 @@ def child_to_parent_messages() -> list[MessageCase]: exp_exceptions=[ValidationError], ), MessageCase( + "unrecognizeable-bad-event", Message( Src=CHILD, Payload=unrecognizeable_bad_event_content, ), exp_exceptions=[ValidationError], ), - MessageCase(Message(Src=CHILD, Payload=snapshot_event)), - MessageCase(Message(Src=CHILD, Payload=StartupEvent())), - MessageCase(Message(Src=CHILD, Payload=ShutdownEvent(Reason="foo"))), + MessageCase("snap-payload-obj", Message(Src=CHILD, Payload=snapshot_event)), + MessageCase("startup-event", Message(Src=CHILD, Payload=StartupEvent())), + MessageCase( + "shutdown-event", Message(Src=CHILD, Payload=ShutdownEvent(Reason="foo")) + ), MessageCase( + "problem-event", Message( Src=CHILD, Payload=ProblemEvent(ProblemType=Problems.error, Summary="foo"), - ) + ), + ), + MessageCase( + "mqtt-connect-event", + Message(Src=CHILD, Payload=MQTTConnectEvent(PeerName=PARENT)), + ), + MessageCase( + "mqtt-conenct-failed-event", + Message(Src=CHILD, Payload=MQTTConnectFailedEvent(PeerName=PARENT)), + ), + MessageCase( + "mqtt-disconnect-event", + Message(Src=CHILD, Payload=MQTTDisconnectEvent(PeerName=PARENT)), + ), + MessageCase( + "mqtt-fully-subscribed-event", + Message(Src=CHILD, Payload=MQTTFullySubscribedEvent(PeerName=PARENT)), ), - MessageCase(Message(Src=CHILD, Payload=MQTTConnectEvent(PeerName=PARENT))), MessageCase( - Message(Src=CHILD, Payload=MQTTConnectFailedEvent(PeerName=PARENT)) + "response-timeout-event", + Message(Src=CHILD, Payload=ResponseTimeoutEvent(PeerName=PARENT)), ), - MessageCase(Message(Src=CHILD, Payload=MQTTDisconnectEvent(PeerName=PARENT))), MessageCase( - Message(Src=CHILD, Payload=MQTTFullySubscribedEvent(PeerName=PARENT)) + "peer-active-event", + Message(Src=CHILD, Payload=PeerActiveEvent(PeerName=PARENT)), ), - MessageCase(Message(Src=CHILD, Payload=ResponseTimeoutEvent(PeerName=PARENT))), - MessageCase(Message(Src=CHILD, Payload=PeerActiveEvent(PeerName=PARENT))), # misc messages - MessageCase(PingMessage(Src=CHILD)), - MessageCase(Message(Src=CHILD, Payload=Ack(AckMessageID="1"))), + MessageCase("ping", PingMessage(Src=CHILD)), + MessageCase("ack", Message(Src=CHILD, Payload=Ack(AckMessageID="1"))), ] @@ -181,96 +198,25 @@ def parent_to_child_messages() -> list[MessageCase]: ).tuple return [ # misc messages - MessageCase(PingMessage(Src=PARENT)), - MessageCase(Message(Src=PARENT, Payload=Ack(AckMessageID="1"))), + MessageCase("ping", PingMessage(Src=PARENT)), + MessageCase("ack", Message(Src=PARENT, Payload=Ack(AckMessageID="1"))), MessageCase( + "snap", Message(Src=PARENT, Payload=snapshot_request.as_dict()), None, snapshot_request, ), - MessageCase(Message(Src=PARENT, Payload=set_relay.as_dict()), None, set_relay), + MessageCase( + "set-relay", + Message(Src=PARENT, Payload=set_relay.as_dict()), + None, + set_relay, + ), ] -def assert_encode_decode( - src_codec: MQTTCodec, - dst_codec: MQTTCodec, - messages: list[MessageCase], -): - errors = [] - for i, case in enumerate(messages): - path_dbg = 0 - # old_len = len(errors) # noqa: ERA001 - try: - decoded = dst_codec.decode( - case.src_message.mqtt_topic(), - src_codec.encode(case.src_message), - ) - except Exception as e: - path_dbg |= 0x00000001 - if type(e) in case.exp_exceptions: - path_dbg |= 0x00000002 - continue - else: - path_dbg |= 0x00000004 - errors.append(i) - if len(errors) == 1: - path_dbg |= 0x00000008 - print(f"FIRST ERROR, at index {i}") - print(f"exp expected exception in {case.exp_exceptions}") - print(f"got: <{type(e)}> <{e}>") - - else: - path_dbg |= 0x00000010 - if case.exp_exceptions: - path_dbg |= 0x00000020 - errors.append(i) - if len(errors) == 1: - path_dbg |= 0x00000040 - print(f"FIRST ERROR, at index {i}") - print(f"exp expected exception in {case.exp_exceptions}") - print(f"got: {decoded}") - else: - path_dbg |= 0x00000080 - decoded = dst_codec.decode( - case.src_message.mqtt_topic(), - src_codec.encode(case.src_message), - ) - if case.exp_message is not None: - path_dbg |= 0x00000100 - if decoded != case.exp_message: - path_dbg |= 0x00000040 - errors.append(i) - if len(errors) == 1: - path_dbg |= 0x00000200 - print(f"FIRST ERROR, at index {i}") - print(f"exp: {case.exp_message}") - print(f"got: {decoded}") - else: - path_dbg |= 0x00000400 - if case.exp_payload is None: - path_dbg |= 0x00000800 - exp_payload = case.src_message.Payload - else: - path_dbg |= 0x00001000 - exp_payload = case.exp_payload - if decoded.Payload != exp_payload: - path_dbg |= 0x00000400 - errors.append(i) - if len(errors) == 1: - path_dbg |= 0x00002000 - print(f"FIRST ERROR, at index {i}") - print(f"exp: {case.exp_payload}") - print(f"got: {decoded.Payload}") - # print(f"{decoded.message_type():50s}: path:0x{path_dbg:08X} {len(errors) == old_len}") # noqa: ERA001 - if errors: - raise ValueError(f"ERROR. Got codec matching errors at indices {errors}") - - -def test_decoder_simple(): +def test_decoder_simple() -> None: child_codec = ChildMQTTCodec() parent_codec = ParentMQTTCodec() - assert_encode_decode(child_codec, parent_codec, child_to_parent_messages()) - assert_encode_decode(parent_codec, child_codec, parent_to_child_messages()) diff --git a/tests/test_message.py b/tests/test_message.py index 442c2e54..a2e464ca 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -43,7 +43,7 @@ def test_naive_payload(): assert m.Header.Dst == "" assert m.Header.MessageType == message_type assert m.Header.MessageId == "" - assert m.Header.AckRequired == False + assert m.Header.AckRequired is False assert m.Header.TypeName == "gridworks.header" # Explicit src, message_type fields, naive payload @@ -60,7 +60,7 @@ def test_naive_payload(): assert m.Header.Dst == "" assert m.Header.MessageType == message_type assert m.Header.MessageId == "" - assert m.Header.AckRequired == False + assert m.Header.AckRequired is False assert m.Header.TypeName == "gridworks.header" # Explicit Header, naive payload @@ -72,18 +72,18 @@ def test_naive_payload(): Payload=NaivePayload(x=1), ) assert m == m2 - assert m.dict() == m2.dict() + assert m.model_dump() == m2.model_dump() # Explicit header from dict, naive payload m2 = Message( Header=Header( Src=src, MessageType=message_type, - ).dict(), + ).model_dump(), Payload=NaivePayload(x=1), ) assert m == m2 - assert m.dict() == m2.dict() + assert m.model_dump() == m2.model_dump() # All header fields in kwargs dst = "bar" @@ -136,13 +136,14 @@ def test_from_payload(): assert m.Header.Dst == "" assert m.Header.MessageType == message_type assert m.Header.MessageId == "" - assert m.Header.AckRequired == False + assert m.Header.AckRequired is False assert m.Header.TypeName == "gridworks.header" # Payload dict provides fields - m2 = Message(Payload=PayloadProvides(Src=src, x=1).dict()) - assert m == m2 - assert m.dict() == m2.dict() + m2 = Message(Payload=PayloadProvides(Src=src, x=1).model_dump()) + assert m.model_dump() == m2.model_dump() + m3 = Message[PayloadProvides](Payload=m2.Payload) + assert m == m3 # *All* header fields from payload object dst = "bar" @@ -169,12 +170,14 @@ def test_from_payload(): assert m.Header.TypeName == "gridworks.header" # *All* header fields from payload dict - m2 = Message(Payload=p.dict()) - assert m == m2 + m2 = Message(Payload=p.model_dump()) + assert m.model_dump() == m2.model_dump() + m3 = Message[PayloadProvidesMore](Payload=m2.Payload) + assert m == m3 # other message type fields for message_type_field_name in PAYLOAD_TYPE_FIELDS: - payload_dict = p.dict() + payload_dict = p.model_dump() del payload_dict["TypeName"] payload_dict[message_type_field_name] = p.TypeName m3 = Message(Payload=payload_dict) diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 88d6c26b..06f274da 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -9,11 +9,10 @@ def test_component_attribute_class_gt_generated() -> None: - # TODO: bump-pydnatic hack; restore this to "component.attribute.class.gt" d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", - "TypeName": "component.attribute.CLASS.gt", + "TypeName": "component.attribute.class.gt", "Version": "000", } diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 8bf8ed13..627efe0b 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -155,7 +155,6 @@ def test_electric_meter_component_gt_generated() -> None: d2["EgaugeIoList"] = [] Maker.dict_to_tuple(d2) - d2 = dict(d) if "ModbusPort" in d2.keys(): del d2["ModbusPort"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index 08be0096..adbdd693 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -14,8 +14,7 @@ def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: "AboutNodeAliasList": ["a.elt1"], "TelemetryNameList": ["ad19e79c"], "ValueList": [18000], - # TODO: bump-pydnatic hack; restore this to "gt.sh.telemetry.from.multipurpose.sensor" - "TypeName": "gt.sh.telemetry.FROM.multipurpose.sensor", + "TypeName": "gt.sh.telemetry.from.multipurpose.sensor", "Version": "100", } diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index b3099a8f..6489052a 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -13,13 +13,12 @@ def test_heartbeat_b_generated() -> None: "FromGNodeAlias": "d1.isone.ver.keene.holly", "FromGNodeInstanceId": "97eba574-bd20-45b5-bf82-9ba2f492d8f6", "MyHex": "a", - "YourLastHex": 2, + "YourLastHex": "2", "LastReceivedTimeUnixMs": 1673635764282, "SendTimeUnixMs": 1673635765317, "StartingOver": False, "TypeName": "heartbeat.b", - # TODO: bump-pydnatic hack; restore this to "000" - "Version": "_001", + "Version": "001", } with pytest.raises(SchemaError): diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index 2206723c..9c8545cf 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -17,11 +17,10 @@ def test_hubitat_gt() -> None: ) assert h.WebListenEnabled is True assert h.listen_path == listen_path_exp - h2 = HubitatGt(WebListenEnabled=False, **h.dict(exclude_unset=True)) + h2 = HubitatGt(WebListenEnabled=False, **h.model_dump(exclude_unset=True)) assert h2.WebListenEnabled is False url = yarl.URL.build(scheme="http", host="192.168.1.20", port=1) assert str(h.listen_url(url)) == f"http://192.168.1.20:1/{listen_path_exp}" - assert h.url_config().to_url() == yarl.URL("http://192.168.1.10") assert h.maker_api_url_config().to_url() == yarl.URL( "http://192.168.1.10/apps/api/1?access_token=foo" From 324fc3152a3f0c0a6de83194d43a9f5a6b30cd45 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 18:53:21 -0400 Subject: [PATCH 053/168] Supress B904 and PLR2004 for now since code generation creates lots of them --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d1bf2ab4..996cd136 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,6 +124,7 @@ show-fixes = true select = ["ALL"] ignore = [ "B027", + "B904", # Suppress for now; lots of them generated by code generation. "COM812", "CPY", "D", @@ -134,6 +135,7 @@ ignore = [ "FA", # We only support Python >= 3.10, so we shouldn't need this "ISC001", "PLR0904", + "PLR2004", # Suppress for now; lots of them generated by code generation. "PLW1514", "TRY003", # Suppress for now; lots of them generated by code generation. "W191", From 6fe3079a1e6e2ded83ed59b3d83ad6837eca4eb5 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:00:01 -0400 Subject: [PATCH 054/168] ruff: ANN204 --- src/gwproto/data_classes/cacs/electric_meter_cac.py | 4 ++-- src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py | 4 ++-- src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py | 4 ++-- src/gwproto/data_classes/cacs/relay_cac.py | 4 ++-- src/gwproto/data_classes/cacs/resistive_heater_cac.py | 4 ++-- src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py | 4 ++-- src/gwproto/data_classes/component.py | 4 ++-- src/gwproto/data_classes/component_attribute_class.py | 4 ++-- .../data_classes/components/electric_meter_component.py | 4 ++-- .../data_classes/components/fibaro_smart_implant_component.py | 2 +- src/gwproto/data_classes/components/hubitat_component.py | 2 +- .../data_classes/components/hubitat_poller_component.py | 2 +- src/gwproto/data_classes/components/hubitat_tank_component.py | 2 +- .../data_classes/components/multipurpose_sensor_component.py | 4 ++-- .../data_classes/components/pipe_flow_sensor_component.py | 4 ++-- src/gwproto/data_classes/components/relay_component.py | 4 ++-- .../data_classes/components/resistive_heater_component.py | 4 ++-- src/gwproto/data_classes/components/rest_poller_component.py | 2 +- .../data_classes/components/simple_temp_sensor_component.py | 4 ++-- src/gwproto/data_classes/components/web_server_component.py | 2 +- src/gwproto/data_classes/hardware_layout.py | 2 +- src/gwproto/data_classes/sh_node.py | 4 ++-- src/gwproto/data_classes/telemetry_tuple.py | 2 +- src/gwproto/default_decoders.py | 4 ++-- src/gwproto/gs/gs_dispatch_maker.py | 2 +- src/gwproto/gs/gs_pwr_maker.py | 2 +- src/gwproto/messages/event.py | 2 +- src/gwproto/types/component_attribute_class_gt.py | 2 +- src/gwproto/types/component_gt.py | 2 +- src/gwproto/types/data_channel.py | 2 +- src/gwproto/types/egauge_io.py | 2 +- src/gwproto/types/egauge_register_config.py | 2 +- src/gwproto/types/electric_meter_cac_gt.py | 2 +- src/gwproto/types/electric_meter_component_gt.py | 2 +- src/gwproto/types/fibaro_smart_implant_cac_gt.py | 2 +- src/gwproto/types/fibaro_smart_implant_component_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 2 +- src/gwproto/types/gt_dispatch_boolean_local.py | 2 +- src/gwproto/types/gt_driver_booleanactuator_cmd.py | 2 +- src/gwproto/types/gt_sh_booleanactuator_cmd_status.py | 2 +- src/gwproto/types/gt_sh_cli_atn_cmd.py | 2 +- src/gwproto/types/gt_sh_multipurpose_telemetry_status.py | 2 +- src/gwproto/types/gt_sh_simple_telemetry_status.py | 2 +- src/gwproto/types/gt_sh_status.py | 2 +- src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py | 2 +- src/gwproto/types/gt_telemetry.py | 2 +- src/gwproto/types/heartbeat_b.py | 2 +- src/gwproto/types/hubitat_cac_gt.py | 2 +- src/gwproto/types/hubitat_component_gt.py | 4 ++-- src/gwproto/types/hubitat_tank_cac_gt.py | 2 +- src/gwproto/types/hubitat_tank_component_gt.py | 2 +- src/gwproto/types/multipurpose_sensor_cac_gt.py | 2 +- src/gwproto/types/multipurpose_sensor_component_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_component_gt.py | 2 +- src/gwproto/types/power_watts.py | 2 +- src/gwproto/types/relay_cac_gt.py | 2 +- src/gwproto/types/relay_component_gt.py | 2 +- src/gwproto/types/resistive_heater_cac_gt.py | 2 +- src/gwproto/types/resistive_heater_component_gt.py | 2 +- src/gwproto/types/rest_poller_cac_gt.py | 2 +- src/gwproto/types/rest_poller_component_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_cac_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_component_gt.py | 2 +- src/gwproto/types/snapshot_spaceheat.py | 2 +- src/gwproto/types/spaceheat_node_gt.py | 2 +- src/gwproto/types/ta_data_channels.py | 2 +- src/gwproto/types/telemetry_reporting_config.py | 2 +- src/gwproto/types/telemetry_snapshot_spaceheat.py | 2 +- tests/dummy_decoders/child/codec.py | 2 +- tests/dummy_decoders/parent/codec.py | 2 +- 71 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py index 643d150f..042a0f90 100644 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ b/src/gwproto/data_classes/cacs/electric_meter_cac.py @@ -18,7 +18,7 @@ def __init__( default_baud: int, display_name: Optional[str] = None, telemetry_name_list: List[TelemetryName] = [], - ): + ) -> None: super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, @@ -31,5 +31,5 @@ def __init__( ElectricMeterCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py index 33ae80e2..08b2c162 100644 --- a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py @@ -20,7 +20,7 @@ def __init__( max_thermistors: Optional[int], display_name: Optional[str] = None, comms_method: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, @@ -37,5 +37,5 @@ def __init__( MultipurposeSensorCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py index 2b9b1eac..1045daf5 100644 --- a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py @@ -15,7 +15,7 @@ def __init__( make_model: MakeModel, display_name: Optional[str] = None, comms_method: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, @@ -27,5 +27,5 @@ def __init__( PipeFlowSensorCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/relay_cac.py b/src/gwproto/data_classes/cacs/relay_cac.py index 21aa57df..0e1a3f33 100644 --- a/src/gwproto/data_classes/cacs/relay_cac.py +++ b/src/gwproto/data_classes/cacs/relay_cac.py @@ -15,7 +15,7 @@ def __init__( make_model: MakeModel, typical_response_time_ms: Optional[int], display_name: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, @@ -26,7 +26,7 @@ def __init__( RelayCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" @property diff --git a/src/gwproto/data_classes/cacs/resistive_heater_cac.py b/src/gwproto/data_classes/cacs/resistive_heater_cac.py index 94744439..27a926f2 100644 --- a/src/gwproto/data_classes/cacs/resistive_heater_cac.py +++ b/src/gwproto/data_classes/cacs/resistive_heater_cac.py @@ -16,7 +16,7 @@ def __init__( nameplate_max_power_w: int, rated_voltage_v: int, display_name: Optional[str] = None, - ): + ) -> None: super( self.__class__, self, @@ -30,5 +30,5 @@ def __init__( ResistiveHeaterCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index 4f0e76f8..8f6578c6 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -19,7 +19,7 @@ def __init__( telemetry_name: TelemetryName, display_name: Optional[str] = None, comms_method: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, @@ -39,5 +39,5 @@ def __init__( "TempSensorCac units must be Fahrenheit, Celsius or Unitless" ) - def __repr__(self): + def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 4a932973..44d06dc1 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -30,7 +30,7 @@ def __init__( component_attribute_class_id: str, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: self.component_id = component_id self.display_name = display_name self.component_attribute_class_id = component_attribute_class_id @@ -40,5 +40,5 @@ def __init__( def component_attribute_class(self) -> ComponentAttributeClass: return ComponentAttributeClass.by_id[self.component_attribute_class_id] - def __repr__(self): + def __repr__(self) -> str: return self.display_name diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 1e387e75..632261b8 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -21,11 +21,11 @@ def __new__(cls, component_attribute_class_id, *args, **kwargs): def __init__( self, component_attribute_class_id: str, display_name: Optional[str] = None - ): + ) -> None: self.component_attribute_class_id = component_attribute_class_id self.display_name = display_name - def __repr__(self): + def __repr__(self) -> str: return ( self.display_name if self.display_name diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index 833dccaa..cbff18ca 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -21,7 +21,7 @@ def __init__( modbus_port: Optional[int] = None, config_list: List[TelemetryReportingConfig] = [], egauge_io_list: List[EgaugeIo] = [], - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -43,5 +43,5 @@ def cac(self) -> ElectricMeterCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py index 7e7cd3be..62b135fa 100644 --- a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py +++ b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py @@ -10,7 +10,7 @@ def __init__( component_attribute_class_id: str, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: super().__init__( component_id=component_id, component_attribute_class_id=component_attribute_class_id, diff --git a/src/gwproto/data_classes/components/hubitat_component.py b/src/gwproto/data_classes/components/hubitat_component.py index 5b54ebbd..29012245 100644 --- a/src/gwproto/data_classes/components/hubitat_component.py +++ b/src/gwproto/data_classes/components/hubitat_component.py @@ -16,7 +16,7 @@ def __init__( hubitat_gt: HubitatGt, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: self.hubitat_gt = hubitat_gt self.web_listener_nodes = set() super().__init__( diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index 574f2a52..a0348113 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -22,7 +22,7 @@ def __init__( poller_gt: HubitatPollerGt, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: self.hubitat_gt = HubitatComponentGt.make_stub(poller_gt.hubitat_component_id) self.poller_gt = poller_gt super().__init__( diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index b68eb33a..99cf8eee 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -31,7 +31,7 @@ def __init__( tank_gt: HubitatTankSettingsGt, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: # Create self.hubitat as a proxy containing only the id # of the hubitat; the actual component data will be resolved # when resolve() is called; Here in the constructor we cannot diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 2dd8ffde..f327bf67 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -19,7 +19,7 @@ def __init__( config_list: List[TelemetryReportingConfig], display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -39,5 +39,5 @@ def cac(self) -> MultipurposeSensorCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 138c70a2..0926b687 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -19,7 +19,7 @@ def __init__( display_name: Optional[str] = None, hw_uid: Optional[str] = None, expected_max_gpm_times100: Optional[int] = None, - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -40,5 +40,5 @@ def cac(self) -> PipeFlowSensorCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 32f04ead..6bd767ec 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -18,7 +18,7 @@ def __init__( display_name: Optional[str] = None, gpio: Optional[int] = None, hw_uid: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -39,5 +39,5 @@ def cac(self) -> RelayCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index 4cc2fc92..ad15749a 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -18,7 +18,7 @@ def __init__( tested_max_cold_milli_ohms: Optional[int] = None, hw_uid: Optional[str] = None, display_name: Optional[str] = None, - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -38,5 +38,5 @@ def cac(self) -> ResistiveHeaterCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/rest_poller_component.py b/src/gwproto/data_classes/components/rest_poller_component.py index d5a80462..8caa79b1 100644 --- a/src/gwproto/data_classes/components/rest_poller_component.py +++ b/src/gwproto/data_classes/components/rest_poller_component.py @@ -14,7 +14,7 @@ def __init__( rest: RESTPollerSettings, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: self.rest = rest super().__init__( display_name=display_name, diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index 049232dc..79b945bc 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -17,7 +17,7 @@ def __init__( display_name: Optional[str] = None, hw_uid: Optional[str] = None, channel: Optional[int] = None, - ): + ) -> None: super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, @@ -36,5 +36,5 @@ def cac(self) -> SimpleTempSensorCac: def make_model(self) -> MakeModel: return self.cac.make_model - def __repr__(self): + def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/web_server_component.py b/src/gwproto/data_classes/components/web_server_component.py index eddf0faa..a228eaf8 100644 --- a/src/gwproto/data_classes/components/web_server_component.py +++ b/src/gwproto/data_classes/components/web_server_component.py @@ -14,7 +14,7 @@ def __init__( web_server_gt: WebServerGt, display_name: Optional[str] = None, hw_uid: Optional[str] = None, - ): + ) -> None: self.web_server_gt = web_server_gt super().__init__( component_id=component_id, diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index ada2dfa4..a270e2fe 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -218,7 +218,7 @@ def __init__( cacs: Optional[dict[str, ComponentAttributeClass]] = None, components: Optional[dict[str, Component]] = None, nodes: Optional[dict[str, ShNode]] = None, - ): + ) -> None: self.layout = copy.deepcopy(layout) if cacs is None: cacs = ComponentAttributeClass.by_id diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 6f269179..22d4e35d 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -30,7 +30,7 @@ def __init__( rated_voltage_v: Optional[int] = None, typical_voltage_v: Optional[int] = None, in_power_metering: Optional[bool] = None, - ): + ) -> None: self.sh_node_id = sh_node_id self.alias = alias self.actor_class = actor_class @@ -43,7 +43,7 @@ def __init__( self.in_power_metering = in_power_metering ShNode.by_id[self.sh_node_id] = self - def __repr__(self): + def __repr__(self) -> str: rs = f"ShNode {self.display_name} => {self.role.value} {self.alias}, " if self.has_actor: rs += " (has actor)" diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index ee216962..e3c069fa 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -9,5 +9,5 @@ class TelemetryTuple(NamedTuple): SensorNode: ShNode TelemetryName: TelemetryName - def __repr__(self): + def __repr__(self) -> str: return f"TT({self.AboutNode.alias} {self.TelemetryName.value} read by {self.SensorNode.alias})" diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index 4e67d557..e2ceca66 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -45,7 +45,7 @@ def decode_to_data_class( class CacDecoder(PydanticTypeNameDecoder): TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") - def __init__(self, model_name: str, **kwargs): + def __init__(self, model_name: str, **kwargs) -> None: if "type_name_regex" not in kwargs: kwargs["type_name_regex"] = CacDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) @@ -63,7 +63,7 @@ def decode_to_data_class( class ComponentDecoder(PydanticTypeNameDecoder): TYPE_NAME_REGEX = re.compile(r".*\.component\.gt") - def __init__(self, model_name: str, **kwargs): + def __init__(self, model_name: str, **kwargs) -> None: if "type_name_regex" not in kwargs: kwargs["type_name_regex"] = ComponentDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) diff --git a/src/gwproto/gs/gs_dispatch_maker.py b/src/gwproto/gs/gs_dispatch_maker.py index 0a11b7da..256e3ddd 100644 --- a/src/gwproto/gs/gs_dispatch_maker.py +++ b/src/gwproto/gs/gs_dispatch_maker.py @@ -8,7 +8,7 @@ class GsDispatch_Maker: type_name = "d" - def __init__(self, relay_state): + def __init__(self, relay_state) -> None: tpl = GsDispatch(RelayState=relay_state) tpl.check_for_errors() self.tuple = tpl diff --git a/src/gwproto/gs/gs_pwr_maker.py b/src/gwproto/gs/gs_pwr_maker.py index dcf46fe5..0393aae1 100644 --- a/src/gwproto/gs/gs_pwr_maker.py +++ b/src/gwproto/gs/gs_pwr_maker.py @@ -8,7 +8,7 @@ class GsPwr_Maker: type_name = "p" - def __init__(self, power): + def __init__(self, power) -> None: tpl = GsPwr(Power=power) tpl.check_for_errors() self.tuple = tpl diff --git a/src/gwproto/messages/event.py b/src/gwproto/messages/event.py index e901b711..cc60d085 100644 --- a/src/gwproto/messages/event.py +++ b/src/gwproto/messages/event.py @@ -27,7 +27,7 @@ class AnyEvent(EventBase, extra="allow"): class EventMessage(Message[EventT], Generic[EventT]): - def __init__(self, **data: Any): + def __init__(self, **data: Any) -> None: if "AckRequired" not in data: data["AckRequired"] = True super().__init__(**data) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 09499be7..eba056f2 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -121,7 +121,7 @@ def __init__( self, component_attribute_class_id: str, display_name: Optional[str], - ): + ) -> None: self.tuple = ComponentAttributeClassGt( ComponentAttributeClassId=component_attribute_class_id, DisplayName=display_name, diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index a0ba3588..f31f1720 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -146,7 +146,7 @@ def __init__( component_attribute_class_id: str, display_name: Optional[str], hw_uid: Optional[str], - ): + ) -> None: self.tuple = ComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py index ef5af792..5861cb39 100644 --- a/src/gwproto/types/data_channel.py +++ b/src/gwproto/types/data_channel.py @@ -137,7 +137,7 @@ def __init__( about_name: str, captured_by_name: str, telemetry_name: EnumTelemetryName, - ): + ) -> None: self.tuple = DataChannel( DisplayName=display_name, AboutName=about_name, diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index bf1df75d..3b509beb 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -115,7 +115,7 @@ def __init__( self, input_config: EgaugeRegisterConfig, output_config: TelemetryReportingConfig, - ): + ) -> None: self.tuple = EgaugeIo( InputConfig=input_config, OutputConfig=output_config, diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index efa1c2ac..98f0c52f 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -130,7 +130,7 @@ def __init__( type: str, # noqa denominator: int, unit: str, - ): + ) -> None: self.tuple = EgaugeRegisterConfig( Address=address, Name=name, diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 8c4700ee..2d2b18b2 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -155,7 +155,7 @@ def __init__( poll_period_ms: int, interface: LocalCommInterface, default_baud: Optional[int], - ): + ) -> None: self.tuple = ElectricMeterCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 4d7527f8..2274991d 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -236,7 +236,7 @@ def __init__( modbus_host: Optional[str], modbus_port: Optional[int], egauge_io_list: List[EgaugeIo], - ): + ) -> None: self.tuple = ElectricMeterComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index b7678477..e7cbda74 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -40,7 +40,7 @@ class FibaroSmartImplantCacGt_Maker: version = "000" tuple: FibaroSmartImplantCacGt - def __init__(self, cac: FibaroSmartImplantCac): + def __init__(self, cac: FibaroSmartImplantCac) -> None: self.tuple = FibaroSmartImplantCacGt.from_data_class(cac) @classmethod diff --git a/src/gwproto/types/fibaro_smart_implant_component_gt.py b/src/gwproto/types/fibaro_smart_implant_component_gt.py index 1771e40b..4649a379 100644 --- a/src/gwproto/types/fibaro_smart_implant_component_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_component_gt.py @@ -47,7 +47,7 @@ class FibaroSmartImplantComponentGt_Maker: version = "000" tuple: FibaroSmartImplantComponentGt - def __init__(self, component: FibaroSmartImplantComponent): + def __init__(self, component: FibaroSmartImplantComponent) -> None: self.tuple = FibaroSmartImplantComponentGt.from_data_class(component) @classmethod diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 74cd34fd..c29b4a37 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -179,7 +179,7 @@ def __init__( from_g_node_instance_id: str, relay_state: int, send_time_unix_ms: int, - ): + ) -> None: self.tuple = GtDispatchBoolean( AboutNodeName=about_node_name, ToGNodeAlias=to_g_node_alias, diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 945b5cda..8fb237ce 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -150,7 +150,7 @@ def __init__( about_node_name: str, from_node_name: str, send_time_unix_ms: int, - ): + ) -> None: self.tuple = GtDispatchBooleanLocal( RelayState=relay_state, AboutNodeName=about_node_name, diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index d445bac9..208598a5 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -128,7 +128,7 @@ def __init__( relay_state: int, sh_node_alias: str, command_time_unix_ms: int, - ): + ) -> None: self.tuple = GtDriverBooleanactuatorCmd( RelayState=relay_state, ShNodeAlias=sh_node_alias, diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 0f875e19..a4341e96 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -128,7 +128,7 @@ def __init__( sh_node_alias: str, relay_state_command_list: List[int], command_time_unix_ms_list: List[int], - ): + ) -> None: self.tuple = GtShBooleanactuatorCmdStatus( ShNodeAlias=sh_node_alias, RelayStateCommandList=relay_state_command_list, diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index 5c9cbce0..ae2f4cd2 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -125,7 +125,7 @@ def __init__( from_g_node_alias: str, send_snapshot: bool, from_g_node_id: str, - ): + ) -> None: self.tuple = GtShCliAtnCmd( FromGNodeAlias=from_g_node_alias, SendSnapshot=send_snapshot, diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index bc137a0e..00110317 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -167,7 +167,7 @@ def __init__( telemetry_name: EnumTelemetryName, value_list: List[int], read_time_unix_ms_list: List[int], - ): + ) -> None: self.tuple = GtShMultipurposeTelemetryStatus( AboutNodeAlias=about_node_alias, SensorNodeAlias=sensor_node_alias, diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 0e6c67aa..33d7ab20 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -153,7 +153,7 @@ def __init__( telemetry_name: EnumTelemetryName, value_list: List[int], read_time_unix_ms_list: List[int], - ): + ) -> None: self.tuple = GtShSimpleTelemetryStatus( ShNodeAlias=sh_node_alias, TelemetryName=telemetry_name, diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index a1067b15..4ab28853 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -200,7 +200,7 @@ def __init__( multipurpose_telemetry_list: List[GtShMultipurposeTelemetryStatus], booleanactuator_cmd_list: List[GtShBooleanactuatorCmdStatus], status_uid: str, - ): + ) -> None: self.tuple = GtShStatus( FromGNodeAlias=from_g_node_alias, FromGNodeId=from_g_node_id, diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 6ff055d7..a91f09be 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -157,7 +157,7 @@ def __init__( about_node_alias_list: List[str], telemetry_name_list: List[TelemetryName], value_list: List[int], - ): + ) -> None: self.tuple = GtShTelemetryFromMultipurposeSensor( ScadaReadTimeUnixMs=scada_read_time_unix_ms, AboutNodeAliasList=about_node_alias_list, diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 372bdafd..3ee0fdc7 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -128,7 +128,7 @@ def __init__( value: int, name: TelemetryName, exponent: int, - ): + ) -> None: self.tuple = GtTelemetry( ScadaReadTimeUnixMs=scada_read_time_unix_ms, Value=value, diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 9402b9c3..1a18c1b7 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -183,7 +183,7 @@ def __init__( last_received_time_unix_ms: int, send_time_unix_ms: int, starting_over: bool, - ): + ) -> None: self.tuple = HeartbeatB( FromGNodeAlias=from_g_node_alias, FromGNodeInstanceId=from_g_node_instance_id, diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index 6e3d2395..adee9f72 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -36,7 +36,7 @@ class HubitatCacGt_Maker: version = "000" tuple: HubitatCacGt - def __init__(self, cac: HubitatCac): + def __init__(self, cac: HubitatCac) -> None: self.tuple = HubitatCacGt.from_data_class(cac) @classmethod diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index 3bbe42dc..16da2ac2 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -93,7 +93,7 @@ class HubitatRESTResolutionSettings: component_gt: HubitatComponentGt maker_api_url_config: URLConfig - def __init__(self, component_gt: HubitatComponentGt): + def __init__(self, component_gt: HubitatComponentGt) -> None: self.component_gt = component_gt self.maker_api_url_config = self.component_gt.maker_api_url_config() @@ -103,7 +103,7 @@ class HubitatComponentGt_Maker: version = "000" tuple: HubitatComponentGt - def __init__(self, component: HubitatComponent): + def __init__(self, component: HubitatComponent) -> None: self.tuple = HubitatComponentGt.from_data_class(component) @classmethod diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index 446c9f81..93f5a7d8 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -36,7 +36,7 @@ class HubitatTankCacGt_Maker: version = "000" tuple: HubitatTankCacGt - def __init__(self, cac: HubitatTankModuleCac): + def __init__(self, cac: HubitatTankModuleCac) -> None: self.tuple = HubitatTankCacGt.from_data_class(cac) @classmethod diff --git a/src/gwproto/types/hubitat_tank_component_gt.py b/src/gwproto/types/hubitat_tank_component_gt.py index 1ba63732..32a0e6a3 100644 --- a/src/gwproto/types/hubitat_tank_component_gt.py +++ b/src/gwproto/types/hubitat_tank_component_gt.py @@ -50,7 +50,7 @@ class HubitatTankComponentGt_Maker: version = "000" tuple: HubitatTankComponentGt - def __init__(self, component: HubitatTankComponent): + def __init__(self, component: HubitatTankComponent) -> None: self.tuple = HubitatTankComponentGt.from_data_class(component) @classmethod diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 7f7416f2..8ae1e689 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -170,7 +170,7 @@ def __init__( max_thermistors: Optional[int], display_name: Optional[str], comms_method: Optional[str], - ): + ) -> None: self.tuple = MultipurposeSensorCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 0575c756..4f049be2 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -165,7 +165,7 @@ def __init__( config_list: List[TelemetryReportingConfig], hw_uid: Optional[str], display_name: Optional[str], - ): + ) -> None: self.tuple = MultipurposeSensorComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 6a69c41d..0b29a35d 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -125,7 +125,7 @@ def __init__( make_model: EnumMakeModel, display_name: Optional[str], comms_method: Optional[str], - ): + ) -> None: self.tuple = PipeFlowSensorCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index caec0307..339d2758 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -159,7 +159,7 @@ def __init__( display_name: Optional[str], hw_uid: Optional[str], expected_max_gpm_times100: Optional[int], - ): + ) -> None: self.tuple = PipeFlowSensorComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 52fc92f5..363a3150 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -92,7 +92,7 @@ class PowerWatts_Maker: def __init__( self, watts: int, - ): + ) -> None: self.tuple = PowerWatts( Watts=watts, ) diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 143f34e5..d7e2d070 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -123,7 +123,7 @@ def __init__( make_model: EnumMakeModel, display_name: Optional[str], typical_response_time_ms: int, - ): + ) -> None: self.tuple = RelayCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 4c956515..5884935d 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -158,7 +158,7 @@ def __init__( gpio: Optional[int], hw_uid: Optional[str], normally_open: bool, - ): + ) -> None: self.tuple = RelayComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 6bc37c67..f7d126f6 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -138,7 +138,7 @@ def __init__( display_name: Optional[str], nameplate_max_power_w: int, rated_voltage_v: int, - ): + ) -> None: self.tuple = ResistiveHeaterCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 2bfc619d..68af952c 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -155,7 +155,7 @@ def __init__( hw_uid: Optional[str], tested_max_hot_milli_ohms: Optional[int], tested_max_cold_milli_ohms: Optional[int], - ): + ) -> None: self.tuple = ResistiveHeaterComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index ac7d838c..48d23ff4 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -36,7 +36,7 @@ class RESTPollerCacGt_Maker: version = "000" tuple: RESTPollerCacGt - def __init__(self, cac: RESTPollerCac): + def __init__(self, cac: RESTPollerCac) -> None: self.tuple = RESTPollerCacGt.from_data_class(cac) @classmethod diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index 0f69626c..7e6de8ff 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -50,7 +50,7 @@ class RESTPollerComponentGt_Maker: version = "000" tuple: RESTPollerComponentGt - def __init__(self, component: RESTPollerComponent): + def __init__(self, component: RESTPollerComponent) -> None: self.tuple = RESTPollerComponentGt.from_data_class(component) @classmethod diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 8d39582e..eabf7c69 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -153,7 +153,7 @@ def __init__( telemetry_name: EnumTelemetryName, display_name: Optional[str], comms_method: Optional[str], - ): + ) -> None: self.tuple = SimpleTempSensorCacGt( ComponentAttributeClassId=component_attribute_class_id, MakeModel=make_model, diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index b702a558..5a60df69 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -152,7 +152,7 @@ def __init__( display_name: Optional[str], hw_uid: Optional[str], channel: Optional[int], - ): + ) -> None: self.tuple = SimpleTempSensorComponentGt( ComponentId=component_id, ComponentAttributeClassId=component_attribute_class_id, diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 0c425633..0e865a32 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -119,7 +119,7 @@ def __init__( from_g_node_alias: str, from_g_node_instance_id: str, snapshot: TelemetrySnapshotSpaceheat, - ): + ) -> None: self.tuple = SnapshotSpaceheat( FromGNodeAlias=from_g_node_alias, FromGNodeInstanceId=from_g_node_instance_id, diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index c5967bd2..b7b120c0 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -203,7 +203,7 @@ def __init__( rated_voltage_v: Optional[int], typical_voltage_v: Optional[int], in_power_metering: Optional[bool], - ): + ) -> None: self.tuple = SpaceheatNodeGt( ShNodeId=sh_node_id, Alias=alias, diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 3a0c8d12..6524cc68 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -168,7 +168,7 @@ def __init__( author: str, channels: List[DataChannel], identifier: str, - ): + ) -> None: self.tuple = TaDataChannels( TerminalAssetGNodeAlias=terminal_asset_g_node_alias, TerminalAssetGNodeId=terminal_asset_g_node_id, diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 86f2f933..0ded573a 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -164,7 +164,7 @@ def __init__( unit: EnumUnit, async_report_threshold: Optional[float], nameplate_max_value: Optional[int], - ): + ) -> None: self.tuple = TelemetryReportingConfig( TelemetryName=telemetry_name, AboutNodeName=about_node_name, diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 09c3238b..69e21a29 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -158,7 +158,7 @@ def __init__( about_node_alias_list: List[str], value_list: List[int], telemetry_name_list: List[TelemetryName], - ): + ) -> None: self.tuple = TelemetrySnapshotSpaceheat( ReportTimeUnixMs=report_time_unix_ms, AboutNodeAliasList=about_node_alias_list, diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index 871dfe0a..6710ef24 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -12,7 +12,7 @@ class ChildMQTTCodec(MQTTCodec): - def __init__(self): + def __init__(self) -> None: super().__init__( Decoders.from_objects( [ diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index 0da3fc88..f51641e1 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -16,7 +16,7 @@ class ParentMQTTCodec(MQTTCodec): - def __init__(self): + def __init__(self) -> None: super().__init__( Decoders.from_objects( [ From 82a6b45920741daf91d3009467a367d2484d93fc Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:02:28 -0400 Subject: [PATCH 055/168] ruff SIM118 --- src/gwproto/data_classes/sh_node.py | 2 +- src/gwproto/types/component_attribute_class_gt.py | 2 +- src/gwproto/types/component_gt.py | 2 +- src/gwproto/types/electric_meter_cac_gt.py | 2 +- src/gwproto/types/electric_meter_component_gt.py | 2 +- src/gwproto/types/multipurpose_sensor_cac_gt.py | 2 +- .../types/multipurpose_sensor_component_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_component_gt.py | 2 +- src/gwproto/types/relay_cac_gt.py | 2 +- src/gwproto/types/relay_component_gt.py | 2 +- src/gwproto/types/resistive_heater_cac_gt.py | 2 +- src/gwproto/types/resistive_heater_component_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_cac_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_component_gt.py | 2 +- src/gwproto/types/spaceheat_node_gt.py | 2 +- tests/data_classes/test_electric_meter_cac.py | 2 +- tests/data_classes/test_electric_meter_component.py | 2 +- tests/types/test_component_attribute_class_gt.py | 2 +- tests/types/test_component_gt.py | 4 ++-- tests/types/test_electric_meter_cac_gt.py | 4 ++-- tests/types/test_electric_meter_component_gt.py | 6 +++--- tests/types/test_multipurpose_sensor_cac_gt.py | 6 +++--- tests/types/test_multipurpose_sensor_component_gt.py | 4 ++-- tests/types/test_pipe_flow_sensor_cac_gt.py | 4 ++-- tests/types/test_pipe_flow_sensor_component_gt.py | 6 +++--- tests/types/test_relay_cac_gt.py | 2 +- tests/types/test_relay_component_gt.py | 6 +++--- tests/types/test_resistive_heater_cac_gt.py | 2 +- tests/types/test_resistive_heater_component_gt.py | 8 ++++---- tests/types/test_simple_temp_sensor_cac_gt.py | 4 ++-- tests/types/test_simple_temp_sensor_component_gt.py | 6 +++--- tests/types/test_spaceheat_node_gt.py | 12 ++++++------ 33 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 22d4e35d..893c49d9 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -61,7 +61,7 @@ def has_actor(self) -> bool: def component(self) -> Optional[Component]: if self.component_id is None: return None - if self.component_id not in Component.by_id.keys(): + if self.component_id not in Component.by_id: raise DataClassLoadingError( f"{self.alias} component {self.component_id} not loaded!" ) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index eba056f2..6e760a5c 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -187,7 +187,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentAttributeClassGt: @classmethod def tuple_to_dc(cls, t: ComponentAttributeClassGt) -> ComponentAttributeClass: - if t.ComponentAttributeClassId in ComponentAttributeClass.by_id.keys(): + if t.ComponentAttributeClassId in ComponentAttributeClass.by_id: dc = ComponentAttributeClass.by_id[t.ComponentAttributeClassId] else: dc = ComponentAttributeClass( diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index f31f1720..df130e9f 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -216,7 +216,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentGt: @classmethod def tuple_to_dc(cls, t: ComponentGt) -> Component: - if t.ComponentId in Component.by_id.keys(): + if t.ComponentId in Component.by_id: dc = Component.by_id[t.ComponentId] else: dc = Component( diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 2d2b18b2..9fb4a0a3 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -245,7 +245,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: @classmethod def tuple_to_dc(cls, t: ElectricMeterCacGt) -> ElectricMeterCac: - if t.ComponentAttributeClassId in ElectricMeterCac.by_id.keys(): + if t.ComponentAttributeClassId in ElectricMeterCac.by_id: dc = ElectricMeterCac.by_id[t.ComponentAttributeClassId] else: dc = ElectricMeterCac( diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 2274991d..5cd9c0b8 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -336,7 +336,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: @classmethod def tuple_to_dc(cls, t: ElectricMeterComponentGt) -> ElectricMeterComponent: - if t.ComponentId in ElectricMeterComponent.by_id.keys(): + if t.ComponentId in ElectricMeterComponent.by_id: dc = ElectricMeterComponent.by_id[t.ComponentId] else: dc = ElectricMeterComponent( diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 8ae1e689..ed9bc501 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -264,7 +264,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: @classmethod def tuple_to_dc(cls, t: MultipurposeSensorCacGt) -> MultipurposeSensorCac: - if t.ComponentAttributeClassId in MultipurposeSensorCac.by_id.keys(): + if t.ComponentAttributeClassId in MultipurposeSensorCac.by_id: dc = MultipurposeSensorCac.by_id[t.ComponentAttributeClassId] else: dc = MultipurposeSensorCac( diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 4f049be2..325aa271 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -254,7 +254,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: def tuple_to_dc( cls, t: MultipurposeSensorComponentGt ) -> MultipurposeSensorComponent: - if t.ComponentId in MultipurposeSensorComponent.by_id.keys(): + if t.ComponentId in MultipurposeSensorComponent.by_id: dc = MultipurposeSensorComponent.by_id[t.ComponentId] else: dc = MultipurposeSensorComponent( diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 0b29a35d..72c30930 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -197,7 +197,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorCacGt: @classmethod def tuple_to_dc(cls, t: PipeFlowSensorCacGt) -> PipeFlowSensorCac: - if t.ComponentAttributeClassId in PipeFlowSensorCac.by_id.keys(): + if t.ComponentAttributeClassId in PipeFlowSensorCac.by_id: dc = PipeFlowSensorCac.by_id[t.ComponentAttributeClassId] else: dc = PipeFlowSensorCac( diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 339d2758..9ed99216 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -236,7 +236,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorComponentGt: @classmethod def tuple_to_dc(cls, t: PipeFlowSensorComponentGt) -> PipeFlowSensorComponent: - if t.ComponentId in PipeFlowSensorComponent.by_id.keys(): + if t.ComponentId in PipeFlowSensorComponent.by_id: dc = PipeFlowSensorComponent.by_id[t.ComponentId] else: dc = PipeFlowSensorComponent( diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index d7e2d070..1c079552 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -197,7 +197,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> RelayCacGt: @classmethod def tuple_to_dc(cls, t: RelayCacGt) -> RelayCac: - if t.ComponentAttributeClassId in RelayCac.by_id.keys(): + if t.ComponentAttributeClassId in RelayCac.by_id: dc = RelayCac.by_id[t.ComponentAttributeClassId] else: dc = RelayCac( diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 5884935d..2dd6100d 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -232,7 +232,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> RelayComponentGt: @classmethod def tuple_to_dc(cls, t: RelayComponentGt) -> RelayComponent: - if t.ComponentId in RelayComponent.by_id.keys(): + if t.ComponentId in RelayComponent.by_id: dc = RelayComponent.by_id[t.ComponentId] else: dc = RelayComponent( diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index f7d126f6..b0f86c4e 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -215,7 +215,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterCacGt: @classmethod def tuple_to_dc(cls, t: ResistiveHeaterCacGt) -> ResistiveHeaterCac: - if t.ComponentAttributeClassId in ResistiveHeaterCac.by_id.keys(): + if t.ComponentAttributeClassId in ResistiveHeaterCac.by_id: dc = ResistiveHeaterCac.by_id[t.ComponentAttributeClassId] else: dc = ResistiveHeaterCac( diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 68af952c..aee748e8 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -227,7 +227,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterComponentGt: @classmethod def tuple_to_dc(cls, t: ResistiveHeaterComponentGt) -> ResistiveHeaterComponent: - if t.ComponentId in ResistiveHeaterComponent.by_id.keys(): + if t.ComponentId in ResistiveHeaterComponent.by_id: dc = ResistiveHeaterComponent.by_id[t.ComponentId] else: dc = ResistiveHeaterComponent( diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index eabf7c69..9afad3b8 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -241,7 +241,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorCacGt: @classmethod def tuple_to_dc(cls, t: SimpleTempSensorCacGt) -> SimpleTempSensorCac: - if t.ComponentAttributeClassId in SimpleTempSensorCac.by_id.keys(): + if t.ComponentAttributeClassId in SimpleTempSensorCac.by_id: dc = SimpleTempSensorCac.by_id[t.ComponentAttributeClassId] else: dc = SimpleTempSensorCac( diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 5a60df69..4257b4cb 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -223,7 +223,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorComponentGt: @classmethod def tuple_to_dc(cls, t: SimpleTempSensorComponentGt) -> SimpleTempSensorComponent: - if t.ComponentId in SimpleTempSensorComponent.by_id.keys(): + if t.ComponentId in SimpleTempSensorComponent.by_id: dc = SimpleTempSensorComponent.by_id[t.ComponentId] else: dc = SimpleTempSensorComponent( diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index b7b120c0..8a54a30b 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -287,7 +287,7 @@ def dict_to_tuple(cls, d: dict[str, Any]) -> SpaceheatNodeGt: @classmethod def tuple_to_dc(cls, t: SpaceheatNodeGt) -> ShNode: - if t.ShNodeId in ShNode.by_id.keys(): + if t.ShNodeId in ShNode.by_id: dc = ShNode.by_id[t.ShNodeId] else: dc = ShNode( diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 226701f3..83ad85e2 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -20,7 +20,7 @@ def test_electric_meter_cac(): } gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) - assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id.keys() + assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] assert dc.__repr__() == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" diff --git a/tests/data_classes/test_electric_meter_component.py b/tests/data_classes/test_electric_meter_component.py index 08653246..443afd4e 100644 --- a/tests/data_classes/test_electric_meter_component.py +++ b/tests/data_classes/test_electric_meter_component.py @@ -47,7 +47,7 @@ def test_electric_meter_component(): } gw_tuple = ElectricMeterComponentGt_Maker.dict_to_tuple(d) - assert gw_tuple.ComponentId in ElectricMeterComponent.by_id.keys() + assert gw_tuple.ComponentId in ElectricMeterComponent.by_id component_as_dc = ElectricMeterComponent.by_id[gw_tuple.ComponentId] assert gw_tuple.HwUid == "9999" assert component_as_dc.hw_uid == "1001ab" diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 06f274da..a22216ba 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -63,7 +63,7 @@ def test_component_attribute_class_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index 8ec1e752..e6c73c2e 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -72,12 +72,12 @@ def test_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index ed550c93..b95f4777 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -94,12 +94,12 @@ def test_electric_meter_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "DefaultBaud" in d2.keys(): + if "DefaultBaud" in d2: del d2["DefaultBaud"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 627efe0b..db802393 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -132,12 +132,12 @@ def test_electric_meter_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) @@ -155,7 +155,7 @@ def test_electric_meter_component_gt_generated() -> None: d2["EgaugeIoList"] = [] Maker.dict_to_tuple(d2) - if "ModbusPort" in d2.keys(): + if "ModbusPort" in d2: del d2["ModbusPort"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index f356edf4..c6b52697 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -103,17 +103,17 @@ def test_multipurpose_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "MaxThermistors" in d2.keys(): + if "MaxThermistors" in d2: del d2["MaxThermistors"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "CommsMethod" in d2.keys(): + if "CommsMethod" in d2: del d2["CommsMethod"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index eaccc910..aa701dff 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -99,12 +99,12 @@ def test_multipurpose_sensor_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index df5160f2..3e06c271 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -73,12 +73,12 @@ def test_pipe_flow_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "CommsMethod" in d2.keys(): + if "CommsMethod" in d2: del d2["CommsMethod"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index f92c64ba..ad298ff5 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -88,17 +88,17 @@ def test_pipe_flow_sensor_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "ExpectedMaxGpmTimes100" in d2.keys(): + if "ExpectedMaxGpmTimes100" in d2: del d2["ExpectedMaxGpmTimes100"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index 3311cc61..a2271cc6 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -78,7 +78,7 @@ def test_relay_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index cec3d0b0..7f5e3a55 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -81,17 +81,17 @@ def test_relay_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "Gpio" in d2.keys(): + if "Gpio" in d2: del d2["Gpio"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index d3dc57f5..44dd46b2 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -85,7 +85,7 @@ def test_resistive_heater_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index 843f6bf9..ef0a1e57 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -76,22 +76,22 @@ def test_resistive_heater_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "TestedMaxHotMilliOhms" in d2.keys(): + if "TestedMaxHotMilliOhms" in d2: del d2["TestedMaxHotMilliOhms"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "TestedMaxColdMilliOhms" in d2.keys(): + if "TestedMaxColdMilliOhms" in d2: del d2["TestedMaxColdMilliOhms"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index 24b34c3e..6b37808b 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -101,12 +101,12 @@ def test_simple_temp_sensor_cac_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "CommsMethod" in d2.keys(): + if "CommsMethod" in d2: del d2["CommsMethod"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_simple_temp_sensor_component_gt.py b/tests/types/test_simple_temp_sensor_component_gt.py index 1c871d9e..bff43fb5 100644 --- a/tests/types/test_simple_temp_sensor_component_gt.py +++ b/tests/types/test_simple_temp_sensor_component_gt.py @@ -74,17 +74,17 @@ def test_simple_temp_sensor_component_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "HwUid" in d2.keys(): + if "HwUid" in d2: del d2["HwUid"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "Channel" in d2.keys(): + if "Channel" in d2: del d2["Channel"] Maker.dict_to_tuple(d2) diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 7122cde7..4fade8e5 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -95,32 +95,32 @@ def test_spaceheat_node_gt_generated() -> None: ###################################### d2 = dict(d) - if "DisplayName" in d2.keys(): + if "DisplayName" in d2: del d2["DisplayName"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "ComponentId" in d2.keys(): + if "ComponentId" in d2: del d2["ComponentId"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "ReportingSamplePeriodS" in d2.keys(): + if "ReportingSamplePeriodS" in d2: del d2["ReportingSamplePeriodS"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "RatedVoltageV" in d2.keys(): + if "RatedVoltageV" in d2: del d2["RatedVoltageV"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "TypicalVoltageV" in d2.keys(): + if "TypicalVoltageV" in d2: del d2["TypicalVoltageV"] Maker.dict_to_tuple(d2) d2 = dict(d) - if "InPowerMetering" in d2.keys(): + if "InPowerMetering" in d2: del d2["InPowerMetering"] Maker.dict_to_tuple(d2) From d2b142b8025bb9f4b721082cdfa14c6612bf15c9 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:04:14 -0400 Subject: [PATCH 056/168] ruff RET504 --- src/gwproto/data_classes/hardware_layout.py | 8 ++------ src/gwproto/types/component_attribute_class_gt.py | 6 ++---- src/gwproto/types/component_gt.py | 6 ++---- src/gwproto/types/egauge_register_config.py | 3 +-- src/gwproto/types/electric_meter_cac_gt.py | 3 +-- src/gwproto/types/electric_meter_component_gt.py | 3 +-- src/gwproto/types/gt_dispatch_boolean.py | 3 +-- src/gwproto/types/gt_dispatch_boolean_local.py | 3 +-- src/gwproto/types/gt_driver_booleanactuator_cmd.py | 3 +-- src/gwproto/types/gt_sh_booleanactuator_cmd_status.py | 3 +-- src/gwproto/types/gt_sh_cli_atn_cmd.py | 3 +-- src/gwproto/types/heartbeat_b.py | 3 +-- src/gwproto/types/multipurpose_sensor_cac_gt.py | 3 +-- src/gwproto/types/multipurpose_sensor_component_gt.py | 3 +-- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 3 +-- src/gwproto/types/pipe_flow_sensor_component_gt.py | 6 ++---- src/gwproto/types/power_watts.py | 3 +-- src/gwproto/types/relay_cac_gt.py | 3 +-- src/gwproto/types/relay_component_gt.py | 6 ++---- src/gwproto/types/resistive_heater_cac_gt.py | 3 +-- src/gwproto/types/resistive_heater_component_gt.py | 6 ++---- src/gwproto/types/simple_temp_sensor_cac_gt.py | 3 +-- src/gwproto/types/simple_temp_sensor_component_gt.py | 6 ++---- src/gwproto/types/spaceheat_node_gt.py | 3 +-- 24 files changed, 31 insertions(+), 64 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index a270e2fe..36a7ef7f 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -446,10 +446,7 @@ def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: @cached_property def power_meter_node(self) -> ShNode: """Schema for input data enforces exactly one Spaceheat Node with role PowerMeter""" - power_meter_node = list( - filter(lambda x: x.role == Role.PowerMeter, self.nodes.values()) - )[0] - return power_meter_node + return list(filter(lambda x: x.role == Role.PowerMeter, self.nodes.values()))[0] @cached_property def power_meter_component(self) -> ElectricMeterComponent: @@ -457,8 +454,7 @@ def power_meter_component(self) -> ElectricMeterComponent: raise ValueError( f"ERROR. power_meter_node {self.power_meter_node} has no component." ) - c = typing.cast(ElectricMeterComponent, self.power_meter_node.component) - return c + return typing.cast(ElectricMeterComponent, self.power_meter_node.component) @cached_property def power_meter_cac(self) -> ElectricMeterCac: diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 6e760a5c..b174b5ee 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -75,7 +75,7 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"}, @@ -83,7 +83,6 @@ def as_dict(self) -> Dict[str, Any]: ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -198,11 +197,10 @@ def tuple_to_dc(cls, t: ComponentAttributeClassGt) -> ComponentAttributeClass: @classmethod def dc_to_tuple(cls, dc: ComponentAttributeClass) -> ComponentAttributeClassGt: - t = ComponentAttributeClassGt_Maker( + return ComponentAttributeClassGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ComponentAttributeClass: diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index df130e9f..11bd2a61 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -98,7 +98,7 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"}, @@ -106,7 +106,6 @@ def as_dict(self) -> Dict[str, Any]: ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -229,13 +228,12 @@ def tuple_to_dc(cls, t: ComponentGt) -> Component: @classmethod def dc_to_tuple(cls, dc: Component) -> ComponentGt: - t = ComponentGt_Maker( + return ComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, hw_uid=dc.hw_uid, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> Component: diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index 98f0c52f..8a33723c 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -81,14 +81,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 9fb4a0a3..4907b443 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -261,7 +261,7 @@ def tuple_to_dc(cls, t: ElectricMeterCacGt) -> ElectricMeterCac: @classmethod def dc_to_tuple(cls, dc: ElectricMeterCac) -> ElectricMeterCacGt: - t = ElectricMeterCacGt_Maker( + return ElectricMeterCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, display_name=dc.display_name, @@ -270,7 +270,6 @@ def dc_to_tuple(cls, dc: ElectricMeterCac) -> ElectricMeterCacGt: interface=dc.interface, default_baud=dc.default_baud, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ElectricMeterCac: diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 5cd9c0b8..dbb52880 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -353,7 +353,7 @@ def tuple_to_dc(cls, t: ElectricMeterComponentGt) -> ElectricMeterComponent: @classmethod def dc_to_tuple(cls, dc: ElectricMeterComponent) -> ElectricMeterComponentGt: - t = ElectricMeterComponentGt_Maker( + return ElectricMeterComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, @@ -363,7 +363,6 @@ def dc_to_tuple(cls, dc: ElectricMeterComponent) -> ElectricMeterComponentGt: modbus_port=dc.modbus_port, egauge_io_list=dc.egauge_io_list, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ElectricMeterComponent: diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index c29b4a37..5c63a03e 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -130,14 +130,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 8fb237ce..9cbbc2be 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -103,14 +103,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 208598a5..a4a0b42e 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -82,14 +82,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index a4341e96..226c498c 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -82,14 +82,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index ae2f4cd2..7e167e1f 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -79,14 +79,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 1a18c1b7..4ee723e2 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -133,14 +133,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index ed9bc501..d0630904 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -282,7 +282,7 @@ def tuple_to_dc(cls, t: MultipurposeSensorCacGt) -> MultipurposeSensorCac: @classmethod def dc_to_tuple(cls, dc: MultipurposeSensorCac) -> MultipurposeSensorCacGt: - t = MultipurposeSensorCacGt_Maker( + return MultipurposeSensorCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, poll_period_ms=dc.poll_period_ms, @@ -293,7 +293,6 @@ def dc_to_tuple(cls, dc: MultipurposeSensorCac) -> MultipurposeSensorCacGt: display_name=dc.display_name, comms_method=dc.comms_method, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> MultipurposeSensorCac: diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 325aa271..bfa4c230 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -271,7 +271,7 @@ def tuple_to_dc( def dc_to_tuple( cls, dc: MultipurposeSensorComponent ) -> MultipurposeSensorComponentGt: - t = MultipurposeSensorComponentGt_Maker( + return MultipurposeSensorComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, channel_list=dc.channel_list, @@ -279,7 +279,6 @@ def dc_to_tuple( hw_uid=dc.hw_uid, display_name=dc.display_name, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> MultipurposeSensorComponent: diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 72c30930..28ca56c5 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -210,13 +210,12 @@ def tuple_to_dc(cls, t: PipeFlowSensorCacGt) -> PipeFlowSensorCac: @classmethod def dc_to_tuple(cls, dc: PipeFlowSensorCac) -> PipeFlowSensorCacGt: - t = PipeFlowSensorCacGt_Maker( + return PipeFlowSensorCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, display_name=dc.display_name, comms_method=dc.comms_method, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> PipeFlowSensorCac: diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 9ed99216..d87cde2f 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -109,14 +109,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -252,7 +251,7 @@ def tuple_to_dc(cls, t: PipeFlowSensorComponentGt) -> PipeFlowSensorComponent: @classmethod def dc_to_tuple(cls, dc: PipeFlowSensorComponent) -> PipeFlowSensorComponentGt: - t = PipeFlowSensorComponentGt_Maker( + return PipeFlowSensorComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, i2c_address=dc.i2c_address, @@ -261,7 +260,6 @@ def dc_to_tuple(cls, dc: PipeFlowSensorComponent) -> PipeFlowSensorComponentGt: hw_uid=dc.hw_uid, expected_max_gpm_times100=dc.expected_max_gpm_times100, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> PipeFlowSensorComponent: diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 363a3150..f96182b7 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -48,14 +48,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 1c079552..e6d2a959 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -210,13 +210,12 @@ def tuple_to_dc(cls, t: RelayCacGt) -> RelayCac: @classmethod def dc_to_tuple(cls, dc: RelayCac) -> RelayCacGt: - t = RelayCacGt_Maker( + return RelayCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, display_name=dc.display_name, typical_response_time_ms=dc.typical_response_time_ms, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> RelayCac: diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 2dd6100d..71915bd0 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -109,14 +109,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -247,7 +246,7 @@ def tuple_to_dc(cls, t: RelayComponentGt) -> RelayComponent: @classmethod def dc_to_tuple(cls, dc: RelayComponent) -> RelayComponentGt: - t = RelayComponentGt_Maker( + return RelayComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, @@ -255,7 +254,6 @@ def dc_to_tuple(cls, dc: RelayComponent) -> RelayComponentGt: hw_uid=dc.hw_uid, normally_open=dc.normally_open, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> RelayComponent: diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index b0f86c4e..2af4ed59 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -229,14 +229,13 @@ def tuple_to_dc(cls, t: ResistiveHeaterCacGt) -> ResistiveHeaterCac: @classmethod def dc_to_tuple(cls, dc: ResistiveHeaterCac) -> ResistiveHeaterCacGt: - t = ResistiveHeaterCacGt_Maker( + return ResistiveHeaterCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, display_name=dc.display_name, nameplate_max_power_w=dc.nameplate_max_power_w, rated_voltage_v=dc.rated_voltage_v, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ResistiveHeaterCac: diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index aee748e8..63472494 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -106,14 +106,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -242,7 +241,7 @@ def tuple_to_dc(cls, t: ResistiveHeaterComponentGt) -> ResistiveHeaterComponent: @classmethod def dc_to_tuple(cls, dc: ResistiveHeaterComponent) -> ResistiveHeaterComponentGt: - t = ResistiveHeaterComponentGt_Maker( + return ResistiveHeaterComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, @@ -250,7 +249,6 @@ def dc_to_tuple(cls, dc: ResistiveHeaterComponent) -> ResistiveHeaterComponentGt tested_max_hot_milli_ohms=dc.tested_max_hot_milli_ohms, tested_max_cold_milli_ohms=dc.tested_max_cold_milli_ohms, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ResistiveHeaterComponent: diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 9afad3b8..cd48fe0e 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -258,7 +258,7 @@ def tuple_to_dc(cls, t: SimpleTempSensorCacGt) -> SimpleTempSensorCac: @classmethod def dc_to_tuple(cls, dc: SimpleTempSensorCac) -> SimpleTempSensorCacGt: - t = SimpleTempSensorCacGt_Maker( + return SimpleTempSensorCacGt_Maker( component_attribute_class_id=dc.component_attribute_class_id, make_model=dc.make_model, typical_response_time_ms=dc.typical_response_time_ms, @@ -268,7 +268,6 @@ def dc_to_tuple(cls, dc: SimpleTempSensorCac) -> SimpleTempSensorCacGt: display_name=dc.display_name, comms_method=dc.comms_method, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> SimpleTempSensorCac: diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 4257b4cb..8fe35d18 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -104,14 +104,13 @@ def as_dict(self) -> Dict[str, Any]: It also applies these changes recursively to sub-types. """ - d = { + return { key: value for key, value in self.model_dump( include=self.model_fields_set | {"TypeName", "Version"} ).items() if value is not None } - return d def as_type(self) -> bytes: """ @@ -237,14 +236,13 @@ def tuple_to_dc(cls, t: SimpleTempSensorComponentGt) -> SimpleTempSensorComponen @classmethod def dc_to_tuple(cls, dc: SimpleTempSensorComponent) -> SimpleTempSensorComponentGt: - t = SimpleTempSensorComponentGt_Maker( + return SimpleTempSensorComponentGt_Maker( component_id=dc.component_id, component_attribute_class_id=dc.component_attribute_class_id, display_name=dc.display_name, hw_uid=dc.hw_uid, channel=dc.channel, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> SimpleTempSensorComponent: diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 8a54a30b..fb009b6b 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -306,7 +306,7 @@ def tuple_to_dc(cls, t: SpaceheatNodeGt) -> ShNode: @classmethod def dc_to_tuple(cls, dc: ShNode) -> SpaceheatNodeGt: - t = SpaceheatNodeGt_Maker( + return SpaceheatNodeGt_Maker( sh_node_id=dc.sh_node_id, alias=dc.alias, actor_class=dc.actor_class, @@ -318,7 +318,6 @@ def dc_to_tuple(cls, dc: ShNode) -> SpaceheatNodeGt: typical_voltage_v=dc.typical_voltage_v, in_power_metering=dc.in_power_metering, ).tuple - return t @classmethod def type_to_dc(cls, t: str) -> ShNode: From abe0742c748f2d68cdc2426e4ae19587313690ca Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:05:36 -0400 Subject: [PATCH 057/168] ruff ANN201 --- .../data_classes/components/hubitat_poller_component.py | 2 +- .../data_classes/components/hubitat_tank_component.py | 2 +- src/gwproto/data_classes/resolver.py | 3 ++- src/gwproto/gs/gs_dispatch.py | 2 +- src/gwproto/gs/gs_pwr.py | 2 +- src/gwproto/types/hubitat_tank_gt.py | 2 +- src/gwproto/types/rest_poller_gt.py | 2 +- tests/conftest.py | 2 +- tests/data_classes/test_electric_meter_cac.py | 2 +- tests/data_classes/test_electric_meter_component.py | 2 +- tests/dummy_decoders/child/codec.py | 2 +- tests/dummy_decoders/parent/codec.py | 2 +- tests/test_gs_dispatch.py | 2 +- tests/test_gs_pwr.py | 2 +- tests/test_message.py | 8 ++++---- tests/test_misc/test_flush_and_load_house.py | 4 ++-- tests/test_topic.py | 4 ++-- tests/utils/__init__.py | 8 ++++---- 18 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index a0348113..d4645f69 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -45,7 +45,7 @@ def resolve( node_name: str, nodes: dict[str, ShNode], components: dict[str, Component], - ): + ) -> None: if self._rest is not None: raise ValueError( f"resolve() must be called exactly once. " diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index 99cf8eee..9496c829 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -53,7 +53,7 @@ def resolve( tank_node_name: str, nodes: dict[str, ShNode], components: dict[str, Component], - ): + ) -> None: hubitat_component = HubitatComponentGt.from_component_id( self.hubitat.ComponentId, components ) diff --git a/src/gwproto/data_classes/resolver.py b/src/gwproto/data_classes/resolver.py index a7ebc6be..eb01a8e7 100644 --- a/src/gwproto/data_classes/resolver.py +++ b/src/gwproto/data_classes/resolver.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from typing import NoReturn from gwproto.data_classes.component import Component from gwproto.data_classes.sh_node import ShNode @@ -11,5 +12,5 @@ def resolve( node_name: str, nodes: dict[str, ShNode], components: dict[str, Component], - ): + ) -> NoReturn: raise NotImplementedError diff --git a/src/gwproto/gs/gs_dispatch.py b/src/gwproto/gs/gs_dispatch.py index f032c30d..8a1d20f9 100644 --- a/src/gwproto/gs/gs_dispatch.py +++ b/src/gwproto/gs/gs_dispatch.py @@ -5,7 +5,7 @@ class GsDispatch(GsDispatchBase): - def check_for_errors(self): + def check_for_errors(self) -> None: errors = self.derived_errors() + self.hand_coded_errors() if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") diff --git a/src/gwproto/gs/gs_pwr.py b/src/gwproto/gs/gs_pwr.py index bea54761..a57876fc 100644 --- a/src/gwproto/gs/gs_pwr.py +++ b/src/gwproto/gs/gs_pwr.py @@ -5,7 +5,7 @@ class GsPwr(GsPwrBase): - def check_for_errors(self): + def check_for_errors(self) -> None: errors = self.derived_errors() + self.hand_coded_errors() if len(errors) > 0: raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index d8796cef..0c19ace8 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -111,7 +111,7 @@ def access_token(self) -> Optional[str]: return match.group("access_token") return None - def clear_property_cache(self): + def clear_property_cache(self) -> None: if self.rest is not None: self.rest.clear_property_cache() for prop in [ diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index 01105f0e..faac5d57 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -206,7 +206,7 @@ def url(self) -> yarl.URL: url_args = request_args return yarl.URL.build(**url_args) - def clear_property_cache(self): + def clear_property_cache(self) -> None: self.__dict__.pop("url", None) @model_validator(mode="after") diff --git a/tests/conftest.py b/tests/conftest.py index f6da1ae4..b044e44b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,5 +6,5 @@ @pytest.fixture(autouse=True) -def flush_local_registries(): +def flush_local_registries() -> None: flush_all() diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 83ad85e2..1be0dd26 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -6,7 +6,7 @@ # test isolation as per scada -def test_electric_meter_cac(): +def test_electric_meter_cac() -> None: HardwareLayout.load("tests/config/hardware-layout.json") d = { "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", diff --git a/tests/data_classes/test_electric_meter_component.py b/tests/data_classes/test_electric_meter_component.py index 443afd4e..126c9837 100644 --- a/tests/data_classes/test_electric_meter_component.py +++ b/tests/data_classes/test_electric_meter_component.py @@ -8,7 +8,7 @@ # test isolation as per scada -def test_electric_meter_component(): +def test_electric_meter_component() -> None: HardwareLayout.load("tests/config/hardware-layout.json") d = { "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index 6710ef24..46ad230e 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -23,6 +23,6 @@ def __init__(self) -> None: ) ) - def validate_source_alias(self, source_alias: str): + def validate_source_alias(self, source_alias: str) -> None: if source_alias != PARENT: raise Exception(f"alias {source_alias} not my parent!") diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index f51641e1..30ddaa98 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -29,6 +29,6 @@ def __init__(self) -> None: ) ) - def validate_source_alias(self, source_alias: str): + def validate_source_alias(self, source_alias: str) -> None: if source_alias != CHILD: raise Exception(f"alias {source_alias} not my child") diff --git a/tests/test_gs_dispatch.py b/tests/test_gs_dispatch.py index 0613484b..3146bddb 100644 --- a/tests/test_gs_dispatch.py +++ b/tests/test_gs_dispatch.py @@ -3,7 +3,7 @@ from gwproto.messages import GsDispatch_Maker as Maker -def test_gs_dispatch(): +def test_gs_dispatch() -> None: gw_tuple = Maker(relay_state=1).tuple assert Maker.tuple_to_type(gw_tuple) == b"\x01\x00" # type: ignore diff --git a/tests/test_gs_pwr.py b/tests/test_gs_pwr.py index 74d35eb9..92321058 100644 --- a/tests/test_gs_pwr.py +++ b/tests/test_gs_pwr.py @@ -3,7 +3,7 @@ from gwproto.messages import GsPwr_Maker as Maker -def test_gs_pwr(): +def test_gs_pwr() -> None: gw_tuple = Maker(power=3200).tuple assert Maker.tuple_to_type(gw_tuple) == b"\x80\x0c" # type: ignore diff --git a/tests/test_message.py b/tests/test_message.py index a2e464ca..a57700c9 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -12,7 +12,7 @@ class E(enum.Enum): b = 2 -def test_as_enum(): +def test_as_enum() -> None: assert as_enum(1, E) == E.a assert as_enum(2, E) == E.b assert as_enum(3, E) is None @@ -23,7 +23,7 @@ class NaivePayload(BaseModel): x: int -def test_naive_payload(): +def test_naive_payload() -> None: assert Message.type_name() == "gw" # Explicit src, message_type fields, pydantic-known-type payload @@ -121,7 +121,7 @@ class PayloadProvidesMore(PayloadProvides): TypeName: Literal["payload.provides.more"] = "payload.provides.more" -def test_from_payload(): +def test_from_payload() -> None: x = 1 src = "foo" message_type = "payload.provides" @@ -184,7 +184,7 @@ def test_from_payload(): assert m.Header == m3.Header -def test_errors(): +def test_errors() -> None: # no Payload with pytest.raises(ValidationError): Message() diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 7efd13aa..27f7a857 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -8,7 +8,7 @@ from tests.utils import flush_all -def test_flush_and_load_house(): +def test_flush_and_load_house() -> None: """Verify that flush_house() successfully removes all dictionary data from relevant dataclasses, and load_house() successfully loads test objects""" flush_all() @@ -53,7 +53,7 @@ def test_flush_and_load_house(): flush_all() -def test_load_real_house(): +def test_load_real_house() -> None: layout = HardwareLayout( { "MyAtomicTNodeGNode": { diff --git a/tests/test_topic.py b/tests/test_topic.py index a091e013..bd07b82c 100644 --- a/tests/test_topic.py +++ b/tests/test_topic.py @@ -2,14 +2,14 @@ from gwproto import DecodedMQTTTopic, MQTTTopic -def test_mqtt_topic_encode(): +def test_mqtt_topic_encode() -> None: assert MQTTTopic.encode("foo", "bar", "baz") == "foo/bar/baz" assert MQTTTopic.encode("foo.bar", "baz.bla", "bla") == "foo-bar/baz-bla/bla" assert MQTTTopic.encode_subscription("foo", "bar") == "foo/bar/#" assert MQTTTopic.encode_subscription("foo.bar", "baz.bla") == "foo-bar/baz-bla/#" -def test_mqtt_topic_decode(): +def test_mqtt_topic_decode() -> None: with pytest.raises(ValueError): MQTTTopic.decode("") diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 458bc5a2..8b08b12d 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -24,7 +24,7 @@ from gwproto.data_classes.sh_node import ShNode -def flush_components(): +def flush_components() -> None: RelayComponent.by_id = {} ElectricMeterComponent.by_id = {} PipeFlowSensorComponent.by_id = {} @@ -34,7 +34,7 @@ def flush_components(): Component.by_id = {} -def flush_cacs(): +def flush_cacs() -> None: RelayCac.by_id = {} ElectricMeterCac.by_id = {} MultipurposeSensorCac.by_id = {} @@ -44,11 +44,11 @@ def flush_cacs(): ComponentAttributeClass.by_id = {} -def flush_spaceheat_nodes(): +def flush_spaceheat_nodes() -> None: ShNode.by_id = {} -def flush_all(): +def flush_all() -> None: flush_components() flush_cacs() flush_spaceheat_nodes() From c6844b6316d48e6adf1932546ef161093add2d20 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:06:53 -0400 Subject: [PATCH 058/168] ruff SIM103 --- src/gwproto/data_classes/sh_node.py | 4 +-- src/gwproto/property_format.py | 40 ++++++++--------------------- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 893c49d9..eab27496 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -53,9 +53,7 @@ def __repr__(self) -> str: @property def has_actor(self) -> bool: - if self.actor_class == ActorClass.NoActor: - return False - return True + return not self.actor_class == ActorClass.NoActor @property def component(self) -> Optional[Component]: diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 97b84868..2562f9f8 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -68,9 +68,7 @@ def is_hex_char(v: str) -> bool: return False if len(v) > 1: return False - if v not in "0123456789abcdefABCDEF": - return False - return True + return not v not in "0123456789abcdefABCDEF" def is_valid_asa_name(candidate: str) -> bool: @@ -86,9 +84,7 @@ def is_valid_asa_name(candidate: str) -> bool: l = len(candidate) except: # noqa return False - if l > 32: - return False - return True + return not l > 32 def check_is_valid_asa_name(candidate: str) -> None: @@ -105,9 +101,7 @@ def check_is_valid_asa_name(candidate: str) -> None: def is_64_bit_hex(candidate: str) -> bool: if len(candidate) != 8: return False - if not all(c in string.hexdigits for c in candidate): - return False - return True + return all(c in string.hexdigits for c in candidate) def check_is_64_bit_hex(candidate: str) -> None: @@ -118,9 +112,7 @@ def check_is_64_bit_hex(candidate: str) -> None: def is_bit(candidate: int) -> bool: - if candidate not in {0, 1}: - return False - return True + return not candidate not in {0, 1} def check_is_bit(candidate: int) -> None: @@ -152,9 +144,7 @@ def is_left_right_dot(candidate: str) -> bool: for word in x: if not word.isalnum(): return False - if not candidate.islower(): - return False - return True + return candidate.islower() def check_is_left_right_dot(candidate: str) -> None: @@ -204,9 +194,7 @@ def is_lru_alias_format(candidate: str) -> bool: for word in x: if not word.isalnum(): return False - if not candidate.islower(): - return False - return True + return candidate.islower() def is_lrh_alias_format(candidate: str) -> bool: @@ -226,17 +214,13 @@ def is_lrh_alias_format(candidate: str) -> bool: for word in x: if not word.isalnum(): return False - if not candidate.islower(): - return False - return True + return candidate.islower() def is_positive_integer(candidate: int) -> bool: if not isinstance(candidate, int): return False # type: ignore[unreachable] - if candidate <= 0: - return False - return True + return not candidate <= 0 def check_is_positive_integer(candidate: int) -> None: @@ -326,9 +310,7 @@ def is_uuid_canonical_textual(candidate: str) -> bool: return False if len(x[3]) != 4: return False - if len(x[4]) != 12: - return False - return True + return not len(x[4]) != 12 def check_is_uuid_canonical_textual(candidate: str) -> None: @@ -384,6 +366,4 @@ def is_world_instance_name_format(candidate: str) -> bool: root_g_node_alias_words = words[0].split(".") except: # noqa return False - if len(root_g_node_alias_words) > 1: - return False - return True + return not len(root_g_node_alias_words) > 1 From b1ecc6524c89305274867d50e4906b49dc694d8d Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:12:02 -0400 Subject: [PATCH 059/168] ruff B006, C408, PLR1714, RUF005, RUF013, RUF015, SIM201, TRY201 --- .../data_classes/cacs/electric_meter_cac.py | 4 +- .../components/electric_meter_component.py | 8 +++- src/gwproto/data_classes/hardware_layout.py | 45 ++++++++++--------- src/gwproto/data_classes/sh_node.py | 2 +- src/gwproto/property_format.py | 11 +++-- .../types/fibaro_smart_implant_cac_gt.py | 2 +- .../fibaro_smart_implant_component_gt.py | 2 +- src/gwproto/types/hubitat_cac_gt.py | 2 +- src/gwproto/types/hubitat_gt.py | 10 ++--- src/gwproto/types/hubitat_poller_cac_gt.py | 2 +- src/gwproto/types/hubitat_tank_cac_gt.py | 2 +- .../types/hubitat_tank_component_gt.py | 2 +- src/gwproto/types/rest_poller_cac_gt.py | 2 +- src/gwproto/types/rest_poller_gt.py | 20 ++++----- tests/types/test_hubitat_gt.py | 10 ++--- 15 files changed, 66 insertions(+), 58 deletions(-) diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py index 042a0f90..a013b2f3 100644 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ b/src/gwproto/data_classes/cacs/electric_meter_cac.py @@ -17,8 +17,10 @@ def __init__( poll_period_ms: int, default_baud: int, display_name: Optional[str] = None, - telemetry_name_list: List[TelemetryName] = [], + telemetry_name_list: Optional[List[TelemetryName]] = None, ) -> None: + if telemetry_name_list is None: + telemetry_name_list = [] super(self.__class__, self).__init__( component_attribute_class_id=component_attribute_class_id, display_name=display_name, diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index cbff18ca..c10f02a0 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -19,9 +19,13 @@ def __init__( hw_uid: Optional[str] = None, modbus_host: Optional[str] = None, modbus_port: Optional[int] = None, - config_list: List[TelemetryReportingConfig] = [], - egauge_io_list: List[EgaugeIo] = [], + config_list: Optional[List[TelemetryReportingConfig]] = None, + egauge_io_list: Optional[List[EgaugeIo]] = None, ) -> None: + if egauge_io_list is None: + egauge_io_list = [] + if config_list is None: + config_list = [] super(self.__class__, self).__init__( display_name=display_name, component_id=component_id, diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 36a7ef7f..89ffe496 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -74,7 +74,7 @@ def load_cacs( ) -> dict[str, Any]: if errors is None: errors = [] - cacs = dict() + cacs = {} for type_name, maker_class in [ ("RelayCacs", RelayCacGt_Maker), ("ResistiveHeaterCacs", ResistiveHeaterCacGt_Maker), @@ -90,7 +90,7 @@ def load_cacs( ) except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError(type_name, d, e)) if cac_decoder is None: cac_decoder = default_cac_decoder @@ -106,7 +106,7 @@ def load_cacs( cacs[d["ComponentAttributeClassId"]] = cac except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError("OtherCacs", d, e)) return cacs @@ -119,7 +119,7 @@ def load_components( ) -> dict[Any, Any]: if errors is None: errors = [] - components = dict() + components = {} for type_name, maker_class in [ ("RelayComponents", RelayComponentGt_Maker), ("ResistiveHeaterComponents", ResistiveHeaterComponentGt_Maker), @@ -135,7 +135,7 @@ def load_components( ) except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError(type_name, d, e)) if component_decoder is None: component_decoder = default_component_decoder @@ -149,7 +149,7 @@ def load_components( components[d["ComponentId"]] = component except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError("OtherComponents", d, e)) return components @@ -170,7 +170,7 @@ def load_nodes( nodes[node_name] = SpaceheatNodeGt_Maker.dict_to_dc(d) except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError("ShNode", d, e)) return nodes @@ -184,7 +184,7 @@ def resolve_links( if errors is None: errors = [] for node_name, node in nodes.items(): - d = dict(node=dict(name=node_name, node=node)) + d = {"node": {"name": node_name, "node": node}} try: if node.component_id is not None: component = components.get(node.component_id, None) @@ -200,7 +200,7 @@ def resolve_links( ) except Exception as e: if raise_errors: - raise e + raise errors.append(LoadError("ShNode", d, e)) @@ -277,26 +277,26 @@ def load_dict( ) -> "HardwareLayout": if errors is None: errors = [] - load_args = dict( - cacs=load_cacs( + load_args = { + "cacs": load_cacs( layout=layout, raise_errors=raise_errors, errors=errors, cac_decoder=cac_decoder, ), - components=load_components( + "components": load_components( layout=layout, raise_errors=raise_errors, errors=errors, component_decoder=component_decoder, ), - nodes=load_nodes( + "nodes": load_nodes( layout=layout, raise_errors=raise_errors, errors=errors, included_node_names=included_node_names, ), - ) + } resolve_links( load_args["nodes"], load_args["components"], @@ -446,7 +446,7 @@ def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: @cached_property def power_meter_node(self) -> ShNode: """Schema for input data enforces exactly one Spaceheat Node with role PowerMeter""" - return list(filter(lambda x: x.role == Role.PowerMeter, self.nodes.values()))[0] + return next(filter(lambda x: x.role == Role.PowerMeter, self.nodes.values())) @cached_property def power_meter_component(self) -> ElectricMeterComponent: @@ -505,8 +505,8 @@ def my_simple_sensors(self) -> List[ShNode]: return list( filter( lambda x: ( - x.actor_class == ActorClass.SimpleSensor - or x.actor_class == ActorClass.BooleanActuator + x.actor_class + in (ActorClass.SimpleSensor, ActorClass.BooleanActuator) ), all_nodes, ) @@ -518,10 +518,13 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: filter( lambda x: ( ( - x.actor_class == ActorClass.MultipurposeSensor - or x.actor_class == ActorClass.HubitatTankModule - or x.actor_class == ActorClass.HubitatPoller - or x.actor_class == ActorClass.HoneywellThermostat + x.actor_class + in ( + ActorClass.MultipurposeSensor, + ActorClass.HubitatTankModule, + ActorClass.HubitatPoller, + ActorClass.HoneywellThermostat, + ) ) and hasattr(x.component, "config_list") ), diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index eab27496..3c20b4b8 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -53,7 +53,7 @@ def __repr__(self) -> str: @property def has_actor(self) -> bool: - return not self.actor_class == ActorClass.NoActor + return self.actor_class != ActorClass.NoActor @property def component(self) -> Optional[Component]: diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 2562f9f8..b76e9bca 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -310,7 +310,7 @@ def is_uuid_canonical_textual(candidate: str) -> bool: return False if len(x[3]) != 4: return False - return not len(x[4]) != 12 + return len(x[4]) == 12 def check_is_uuid_canonical_textual(candidate: str) -> None: @@ -344,11 +344,10 @@ def check_world_alias_matches_universe(g_node_alias: str, universe: str) -> None """ check_is_left_right_dot(g_node_alias) world_alias = g_node_alias.split(".")[0] - if universe == "dev": - if world_alias[0] != "d": - raise ValueError( - f"World alias for dev universe must start with d. Got {world_alias}" - ) + if universe == "dev" and world_alias[0] != "d": + raise ValueError( + f"World alias for dev universe must start with d. Got {world_alias}" + ) def is_world_instance_name_format(candidate: str) -> bool: diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index e7cbda74..b209dc7c 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -32,7 +32,7 @@ def to_data_class(self) -> FibaroSmartImplantCac: ) def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) class FibaroSmartImplantCacGt_Maker: diff --git a/src/gwproto/types/fibaro_smart_implant_component_gt.py b/src/gwproto/types/fibaro_smart_implant_component_gt.py index 4649a379..895fd9cd 100644 --- a/src/gwproto/types/fibaro_smart_implant_component_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_component_gt.py @@ -17,7 +17,7 @@ class FibaroSmartImplantComponentGt(ComponentGt): Version: Literal["000"] = "000" def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) @classmethod def from_data_class( diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index adee9f72..64a682ec 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -28,7 +28,7 @@ def to_data_class(self) -> HubitatCac: ) def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) class HubitatCacGt_Maker: diff --git a/src/gwproto/types/hubitat_gt.py b/src/gwproto/types/hubitat_gt.py index 019422c7..3fe99f6d 100644 --- a/src/gwproto/types/hubitat_gt.py +++ b/src/gwproto/types/hubitat_gt.py @@ -52,11 +52,11 @@ def devices_url_config(self) -> URLConfig: return config def url_configs(self) -> dict[str, URLConfig]: - return dict( - base=self.url_config(), - maker_api=self.maker_api_url_config(), - devices=self.devices_url_config(), - ) + return { + "base": self.url_config(), + "maker_api": self.maker_api_url_config(), + "devices": self.devices_url_config(), + } def urls(self) -> dict[str, Optional[yarl.URL]]: return { diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index ad2fc768..a0f6c3b7 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -28,7 +28,7 @@ def to_data_class(self) -> HubitatPollerCac: ) def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) class HubitatPollerCacGt_Maker: diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index 93f5a7d8..4342fcb9 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -28,7 +28,7 @@ def to_data_class(self) -> HubitatTankModuleCac: ) def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) class HubitatTankCacGt_Maker: diff --git a/src/gwproto/types/hubitat_tank_component_gt.py b/src/gwproto/types/hubitat_tank_component_gt.py index 32a0e6a3..cf784c46 100644 --- a/src/gwproto/types/hubitat_tank_component_gt.py +++ b/src/gwproto/types/hubitat_tank_component_gt.py @@ -15,7 +15,7 @@ class HubitatTankComponentGt(ComponentGt): Version: Literal["000"] = "000" def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) @classmethod def from_data_class( diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index 48d23ff4..7f91c064 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -28,7 +28,7 @@ def to_data_class(self) -> RESTPollerCac: ) def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) + return hash((type(self), *tuple(self.__dict__.values()))) class RESTPollerCacGt_Maker: diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index faac5d57..0ceee699 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -30,16 +30,16 @@ class URLArgs(BaseModel): def dict_from_url(cls, url: str | yarl.URL) -> dict: if isinstance(url, str): url = yarl.URL(url) - return dict( - scheme=url.scheme, - user=url.user, - password=url.password, - host=url.host, - port=url.port, - path=url.path, - query=list(url.query.items()), - fragment=url.fragment, - ) + return { + "scheme": url.scheme, + "user": url.user, + "password": url.password, + "host": url.host, + "port": url.port, + "path": url.path, + "query": list(url.query.items()), + "fragment": url.fragment, + } @classmethod def from_url(cls, url: str | yarl.URL) -> "URLArgs": diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index 9c8545cf..167404d1 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -29,11 +29,11 @@ def test_hubitat_gt() -> None: "http://192.168.1.10/apps/api/1/devices?access_token=foo" ) url_configs_got = {k: v.to_url() for k, v in h.url_configs().items()} - url_configs_exp = dict( - base=yarl.URL("http://192.168.1.10"), - maker_api=yarl.URL("http://192.168.1.10/apps/api/1?access_token=foo"), - devices=yarl.URL("http://192.168.1.10/apps/api/1/devices?access_token=foo"), - ) + url_configs_exp = { + "base": yarl.URL("http://192.168.1.10"), + "maker_api": yarl.URL("http://192.168.1.10/apps/api/1?access_token=foo"), + "devices": yarl.URL("http://192.168.1.10/apps/api/1/devices?access_token=foo"), + } assert url_configs_got == url_configs_exp assert h.urls() == url_configs_exp device_id = 1 From bd1c833546eed6b2096bb3549cade50f885be561 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:22:24 -0400 Subject: [PATCH 060/168] ruff INP001 --- docs/__init__.py | 0 src/gwproto/data_classes/__init__.py | 0 src/gwproto/data_classes/cacs/__init__.py | 0 src/gwproto/data_classes/components/__init__.py | 0 tests/data_classes/__init__.py | 0 tests/enums/__init__.py | 0 tests/types/__init__.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/__init__.py create mode 100644 src/gwproto/data_classes/__init__.py create mode 100644 src/gwproto/data_classes/cacs/__init__.py create mode 100644 src/gwproto/data_classes/components/__init__.py create mode 100644 tests/data_classes/__init__.py create mode 100644 tests/enums/__init__.py create mode 100644 tests/types/__init__.py diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/gwproto/data_classes/__init__.py b/src/gwproto/data_classes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/gwproto/data_classes/cacs/__init__.py b/src/gwproto/data_classes/cacs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/gwproto/data_classes/components/__init__.py b/src/gwproto/data_classes/components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data_classes/__init__.py b/tests/data_classes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/enums/__init__.py b/tests/enums/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/types/__init__.py b/tests/types/__init__.py new file mode 100644 index 00000000..e69de29b From e1a01e9069f16055d144b45765c73e7b82a6290a Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:22:56 -0400 Subject: [PATCH 061/168] ruff isort --- src/gwproto/data_classes/components/hubitat_component.py | 1 + src/gwproto/data_classes/components/hubitat_poller_component.py | 1 + src/gwproto/data_classes/components/hubitat_tank_component.py | 1 + 3 files changed, 3 insertions(+) diff --git a/src/gwproto/data_classes/components/hubitat_component.py b/src/gwproto/data_classes/components/hubitat_component.py index 29012245..14e46917 100644 --- a/src/gwproto/data_classes/components/hubitat_component.py +++ b/src/gwproto/data_classes/components/hubitat_component.py @@ -1,6 +1,7 @@ from typing import Optional import yarl + from gwproto.data_classes.component import Component from gwproto.types.hubitat_gt import HubitatGt diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index d4645f69..6a897a5b 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -1,6 +1,7 @@ from typing import Optional import yarl + from gwproto.data_classes.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index 9496c829..c386ae71 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -1,6 +1,7 @@ from typing import Optional import yarl + from gwproto.data_classes.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode From 69ca11f826324cc6984f5e791b7949e8f3936d3a Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 19 Aug 2024 19:57:48 -0400 Subject: [PATCH 062/168] ruff: ANN204 --- pyproject.toml | 2 ++ src/gwproto/default_decoders.py | 2 +- src/gwproto/property_format.py | 24 ++++++++++--------- .../types/component_attribute_class_gt.py | 2 +- src/gwproto/types/component_gt.py | 2 +- src/gwproto/types/data_channel.py | 2 +- src/gwproto/types/egauge_io.py | 2 +- src/gwproto/types/egauge_register_config.py | 2 +- src/gwproto/types/electric_meter_cac_gt.py | 2 +- .../types/electric_meter_component_gt.py | 2 +- .../types/fibaro_smart_implant_cac_gt.py | 2 +- .../fibaro_smart_implant_component_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 2 +- .../types/gt_dispatch_boolean_local.py | 2 +- .../types/gt_driver_booleanactuator_cmd.py | 2 +- .../types/gt_sh_booleanactuator_cmd_status.py | 2 +- src/gwproto/types/gt_sh_cli_atn_cmd.py | 2 +- .../gt_sh_multipurpose_telemetry_status.py | 2 +- .../types/gt_sh_simple_telemetry_status.py | 2 +- src/gwproto/types/gt_sh_status.py | 2 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 2 +- src/gwproto/types/gt_telemetry.py | 2 +- src/gwproto/types/heartbeat_b.py | 2 +- src/gwproto/types/hubitat_cac_gt.py | 2 +- src/gwproto/types/hubitat_component_gt.py | 2 +- src/gwproto/types/hubitat_poller_cac_gt.py | 2 +- src/gwproto/types/hubitat_tank_cac_gt.py | 2 +- .../types/hubitat_tank_component_gt.py | 2 +- .../types/multipurpose_sensor_cac_gt.py | 2 +- .../types/multipurpose_sensor_component_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 2 +- .../types/pipe_flow_sensor_component_gt.py | 2 +- src/gwproto/types/power_watts.py | 2 +- src/gwproto/types/relay_cac_gt.py | 2 +- src/gwproto/types/relay_component_gt.py | 2 +- src/gwproto/types/resistive_heater_cac_gt.py | 2 +- .../types/resistive_heater_component_gt.py | 2 +- src/gwproto/types/rest_poller_cac_gt.py | 2 +- .../types/simple_temp_sensor_cac_gt.py | 2 +- .../types/simple_temp_sensor_component_gt.py | 2 +- src/gwproto/types/snapshot_spaceheat.py | 2 +- src/gwproto/types/spaceheat_node_gt.py | 2 +- src/gwproto/types/ta_data_channels.py | 2 +- .../types/telemetry_reporting_config.py | 2 +- .../types/telemetry_snapshot_spaceheat.py | 2 +- src/gwproto/types/web_server_cac_gt.py | 2 +- src/gwproto/types/web_server_component_gt.py | 2 +- 47 files changed, 60 insertions(+), 56 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 996cd136..fa621c1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,6 +134,8 @@ ignore = [ "EM", "FA", # We only support Python >= 3.10, so we shouldn't need this "ISC001", + "N801", + "PGH004", # Suppress for now; lots of them generated by code generation. "PLR0904", "PLR2004", # Suppress for now; lots of them generated by code generation. "PLW1514", diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index e2ceca66..b8d87151 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -13,7 +13,7 @@ import gwproto.types.rest_poller_cac_gt import gwproto.types.rest_poller_component_gt import gwproto.types.web_server_cac_gt -import gwproto.types.web_server_component_gt # noqa +import gwproto.types.web_server_component_gt # noqa: F401 from gwproto.data_classes.component import Component from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.decoders import PydanticTypeNameDecoder diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index b76e9bca..8d9be4d2 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -1,3 +1,5 @@ +# ruff: noqa: ANN401 + import string import struct from typing import Any, Callable, List @@ -81,18 +83,18 @@ def is_valid_asa_name(candidate: str) -> bool: bool: True if a string no more than 32 cars """ try: - l = len(candidate) - except: # noqa + candidate_len = len(candidate) + except: # noqa: E722 return False - return not l > 32 + return not candidate_len > 32 def check_is_valid_asa_name(candidate: str) -> None: try: - l = len(candidate) + candidate_len = len(candidate) except Exception as e: - raise ValueError(f"Not ValidAsaName: {e} /n {candidate} ") - if l > 32: + raise ValueError(f"Not ValidAsaName: {e} /n {candidate} ") from e + if candidate_len > 32: raise ValueError( f"Not ValidAsaName: AsaNames must be <= 32 /n {candidate} is {len(candidate)}" ) @@ -160,8 +162,8 @@ def check_is_left_right_dot(candidate: str) -> None: """ try: x: List[str] = candidate.split(".") - except: - raise ValueError("Failed to seperate into words with split'.'") + except Exception as e: + raise ValueError("Failed to seperate into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -225,7 +227,7 @@ def is_positive_integer(candidate: int) -> bool: def check_is_positive_integer(candidate: int) -> None: if not isinstance(candidate, int): - raise ValueError("Must be an integer") + raise ValueError("Must be an integer") # noqa: TRY004 if candidate <= 0: raise ValueError("Must be positive integer") @@ -271,7 +273,7 @@ def is_unsigned_short(candidate: int) -> bool: def check_is_unsigned_short(candidate: int) -> None: try: struct.pack("H", candidate) - except: + except: # noqa: E722 raise ValueError("requires 0 <= number <= 65535") @@ -286,7 +288,7 @@ def is_short_integer(candidate: int) -> bool: def check_is_short_integer(candidate: int) -> None: try: struct.pack("h", candidate) - except: + except: # noqa: E722 raise ValueError("short format requires (-32767 -1) <= number <= 32767") diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index b174b5ee..d0aa7eba 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -108,7 +108,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index 11bd2a61..ae10c389 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -131,7 +131,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py index 5861cb39..071cffae 100644 --- a/src/gwproto/types/data_channel.py +++ b/src/gwproto/types/data_channel.py @@ -123,7 +123,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index 3b509beb..ea2ec4eb 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -103,7 +103,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index 8a33723c..ecd64dfd 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -113,7 +113,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 4907b443..4f2e7a1e 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -138,7 +138,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index dbb52880..38ead6c9 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -218,7 +218,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index b209dc7c..e7c95d64 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -31,7 +31,7 @@ def to_data_class(self) -> FibaroSmartImplantCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/fibaro_smart_implant_component_gt.py b/src/gwproto/types/fibaro_smart_implant_component_gt.py index 895fd9cd..f3c5aaff 100644 --- a/src/gwproto/types/fibaro_smart_implant_component_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_component_gt.py @@ -16,7 +16,7 @@ class FibaroSmartImplantComponentGt(ComponentGt): ) Version: Literal["000"] = "000" - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) @classmethod diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 5c63a03e..4d9d813a 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -162,7 +162,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 9cbbc2be..53c3615e 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -135,7 +135,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index a4a0b42e..e56ce153 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -114,7 +114,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 226c498c..f3f7c694 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -114,7 +114,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index 7e167e1f..069313de 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -111,7 +111,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 00110317..0f2c67ce 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -152,7 +152,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 33d7ab20..3b4ad69d 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -139,7 +139,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 4ab28853..e2979c1f 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -181,7 +181,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index a91f09be..37abf5a8 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -143,7 +143,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 3ee0fdc7..d451dedf 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -114,7 +114,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 4ee723e2..442735fd 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -165,7 +165,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index 64a682ec..5b65d2f2 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -27,7 +27,7 @@ def to_data_class(self) -> HubitatCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index 16da2ac2..c8d85954 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -16,7 +16,7 @@ class HubitatComponentGt(ComponentGt): TypeName: Literal["hubitat.component.gt"] = "hubitat.component.gt" Version: Literal["000"] = "000" - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa def url_config(self) -> URLConfig: diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index a0f6c3b7..1032539c 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -27,7 +27,7 @@ def to_data_class(self) -> HubitatPollerCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index 4342fcb9..d238d3ef 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -27,7 +27,7 @@ def to_data_class(self) -> HubitatTankModuleCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/hubitat_tank_component_gt.py b/src/gwproto/types/hubitat_tank_component_gt.py index cf784c46..771796dd 100644 --- a/src/gwproto/types/hubitat_tank_component_gt.py +++ b/src/gwproto/types/hubitat_tank_component_gt.py @@ -14,7 +14,7 @@ class HubitatTankComponentGt(ComponentGt): TypeName: Literal["hubitat.tank.component.gt"] = "hubitat.tank.component.gt" Version: Literal["000"] = "000" - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) @classmethod diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index d0630904..b6bb9263 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -151,7 +151,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index bfa4c230..f89637dc 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -149,7 +149,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 28ca56c5..930db363 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -111,7 +111,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index d87cde2f..635439a1 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -141,7 +141,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index f96182b7..8c237f89 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -80,7 +80,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index e6d2a959..01914451 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -109,7 +109,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 71915bd0..1b2a2ef2 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -141,7 +141,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 2af4ed59..a22cd657 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -123,7 +123,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 63472494..663be4c8 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -138,7 +138,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index 7f91c064..bca855f1 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -27,7 +27,7 @@ def to_data_class(self) -> RESTPollerCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index cd48fe0e..d56f3bc1 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -135,7 +135,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 8fe35d18..81000c43 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -136,7 +136,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 0e865a32..ef23cd8b 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -106,7 +106,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index fb009b6b..23f29e69 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -183,7 +183,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 6524cc68..e45461a5 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -152,7 +152,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 0ded573a..2298d53b 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -146,7 +146,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 69e21a29..ca91264c 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -144,7 +144,7 @@ def as_type(self) -> bytes: json_string = json.dumps(self.as_dict()) return json_string.encode("utf-8") - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index 3c2925d3..ddc17579 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -26,5 +26,5 @@ def to_data_class(self) -> WebServerCac: display_name=self.DisplayName, ) - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/types/web_server_component_gt.py b/src/gwproto/types/web_server_component_gt.py index addd22b2..45772558 100644 --- a/src/gwproto/types/web_server_component_gt.py +++ b/src/gwproto/types/web_server_component_gt.py @@ -12,7 +12,7 @@ class WebServerComponentGt(ComponentGt): TypeName: Literal["web.server.component.gt"] = "web.server.component.gt" Version: Literal["000"] = "000" - def __hash__(self): + def __hash__(self) -> int: return hash((type(self),) + tuple(self.__dict__.values())) # noqa @classmethod From a4497b2b9bcd01207d86f8dc8bbdfe420c655d92 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 09:42:56 -0400 Subject: [PATCH 063/168] ruff: suppress warnings --- src/gwproto/default_decoders.py | 44 +++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index b8d87151..23a8f8db 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -1,18 +1,20 @@ +# ruff: noqa: ANN401, RUF100 + import re import typing -from typing import Type, TypeVar - -import gwproto.types.fibaro_smart_implant_cac_gt -import gwproto.types.fibaro_smart_implant_component_gt -import gwproto.types.hubitat_cac_gt -import gwproto.types.hubitat_component_gt -import gwproto.types.hubitat_poller_cac_gt -import gwproto.types.hubitat_poller_component_gt -import gwproto.types.hubitat_tank_cac_gt -import gwproto.types.hubitat_tank_component_gt -import gwproto.types.rest_poller_cac_gt -import gwproto.types.rest_poller_component_gt -import gwproto.types.web_server_cac_gt +from typing import Any, Type, TypeVar + +import gwproto.types.fibaro_smart_implant_cac_gt # noqa: F401 +import gwproto.types.fibaro_smart_implant_component_gt # noqa: F401 +import gwproto.types.hubitat_cac_gt # noqa: F401 +import gwproto.types.hubitat_component_gt # noqa: F401 +import gwproto.types.hubitat_poller_cac_gt # noqa: F401 +import gwproto.types.hubitat_poller_component_gt # noqa: F401 +import gwproto.types.hubitat_tank_cac_gt # noqa: F401 +import gwproto.types.hubitat_tank_component_gt # noqa: F401 +import gwproto.types.rest_poller_cac_gt # noqa: F401 +import gwproto.types.rest_poller_component_gt # noqa: F401 +import gwproto.types.web_server_cac_gt # noqa: F401 import gwproto.types.web_server_component_gt # noqa: F401 from gwproto.data_classes.component import Component from gwproto.data_classes.component_attribute_class import ComponentAttributeClass @@ -24,8 +26,8 @@ def decode_to_data_class( decoded_gt: typing.Any, return_type: Type[T], - allow_missing_func: bool = True, - allow_non_instance: bool = False, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 + allow_non_instance: bool = False, # noqa: FBT001, FBT002 ) -> T: if hasattr(decoded_gt, "to_data_class"): data_class = decoded_gt.to_data_class() @@ -45,13 +47,15 @@ def decode_to_data_class( class CacDecoder(PydanticTypeNameDecoder): TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") - def __init__(self, model_name: str, **kwargs) -> None: + def __init__(self, model_name: str, **kwargs: Any) -> None: if "type_name_regex" not in kwargs: kwargs["type_name_regex"] = CacDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) def decode_to_data_class( - self, data: dict, allow_missing_func: bool = True + self, + data: dict, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 ) -> ComponentAttributeClass: return decode_to_data_class( decoded_gt=self.decode_obj(data), @@ -63,13 +67,15 @@ def decode_to_data_class( class ComponentDecoder(PydanticTypeNameDecoder): TYPE_NAME_REGEX = re.compile(r".*\.component\.gt") - def __init__(self, model_name: str, **kwargs) -> None: + def __init__(self, model_name: str, **kwargs: Any) -> None: if "type_name_regex" not in kwargs: kwargs["type_name_regex"] = ComponentDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) def decode_to_data_class( - self, data: dict, allow_missing_func: bool = True + self, + data: dict, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 ) -> Component: return decode_to_data_class( decoded_gt=self.decode_obj(data), From 702b20957ae07245dbb67201652b5a849b31161c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 09:51:59 -0400 Subject: [PATCH 064/168] Update ruff to 0.6.1 --- .pre-commit-config.yaml | 2 +- poetry.lock | 78 +++++-------------- pyproject.toml | 2 +- src/gwproto/data_classes/hardware_layout.py | 12 +-- tests/dummy_decoders/child/codec.py | 1 - tests/dummy_decoders/parent/codec.py | 1 - tests/test_decoders.py | 4 +- tests/test_gs_dispatch.py | 1 + tests/test_gs_pwr.py | 1 + tests/test_message.py | 3 +- tests/test_misc/test_flush_and_load_house.py | 1 - tests/test_topic.py | 1 + .../test_component_attribute_class_gt.py | 3 +- tests/types/test_component_gt.py | 3 +- tests/types/test_data_channel.py | 3 +- tests/types/test_egauge_io.py | 3 +- tests/types/test_egauge_register_config.py | 3 +- tests/types/test_electric_meter_cac_gt.py | 3 +- .../types/test_electric_meter_component_gt.py | 3 +- tests/types/test_gt_dispatch_boolean.py | 3 +- tests/types/test_gt_dispatch_boolean_local.py | 3 +- .../test_gt_driver_booleanactuator_cmd.py | 3 +- .../test_gt_sh_booleanactuator_cmd_status.py | 3 +- tests/types/test_gt_sh_cli_atn_cmd.py | 3 +- ...est_gt_sh_multipurpose_telemetry_status.py | 3 +- .../test_gt_sh_simple_telemetry_status.py | 3 +- tests/types/test_gt_sh_status.py | 3 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 3 +- tests/types/test_gt_telemetry.py | 3 +- tests/types/test_heartbeat_b.py | 3 +- tests/types/test_hubitat_gt.py | 1 + .../types/test_multipurpose_sensor_cac_gt.py | 3 +- .../test_multipurpose_sensor_component_gt.py | 3 +- tests/types/test_pipe_flow_sensor_cac_gt.py | 3 +- .../test_pipe_flow_sensor_component_gt.py | 3 +- tests/types/test_power_watts.py | 3 +- tests/types/test_relay_cac_gt.py | 3 +- tests/types/test_relay_component_gt.py | 3 +- tests/types/test_resistive_heater_cac_gt.py | 3 +- .../test_resistive_heater_component_gt.py | 3 +- tests/types/test_simple_temp_sensor_cac_gt.py | 3 +- .../test_simple_temp_sensor_component_gt.py | 3 +- tests/types/test_snapshot_spaceheat.py | 3 +- tests/types/test_spaceheat_node_gt.py | 3 +- tests/types/test_ta_data_channels.py | 3 +- .../types/test_telemetry_reporting_config.py | 3 +- .../test_telemetry_snapshot_spaceheat.py | 3 +- 47 files changed, 103 insertions(+), 107 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f72c3cb0..9cba0376 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: args: [--py37-plus] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.7 + rev: v0.6.1 hooks: - id: ruff args: ["--select", "I"] diff --git a/poetry.lock b/poetry.lock index ef893877..748e558b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -34,10 +34,8 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] @@ -323,9 +321,6 @@ files = [ {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -351,20 +346,6 @@ files = [ {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "filelock" version = "3.15.4" @@ -800,7 +781,6 @@ files = [ [package.dependencies] mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.6.0" [package.extras] @@ -978,7 +958,6 @@ files = [ [package.dependencies] "ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pycodestyle" @@ -1152,11 +1131,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1391,29 +1368,29 @@ files = [ [[package]] name = "ruff" -version = "0.5.7" +version = "0.6.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, - {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, - {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, - {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, - {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, - {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, - {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, + {file = "ruff-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9"}, + {file = "ruff-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae"}, + {file = "ruff-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e"}, + {file = "ruff-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15"}, + {file = "ruff-0.6.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb"}, + {file = "ruff-0.6.1-py3-none-win32.whl", hash = "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4"}, + {file = "ruff-0.6.1-py3-none-win_amd64.whl", hash = "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b"}, + {file = "ruff-0.6.1-py3-none-win_arm64.whl", hash = "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014"}, + {file = "ruff-0.6.1.tar.gz", hash = "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436"}, ] [[package]] @@ -1499,7 +1476,6 @@ sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" -tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] @@ -1683,17 +1659,6 @@ files = [ {file = "tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367"}, ] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "typeguard" version = "4.3.0" @@ -1782,7 +1747,6 @@ files = [ [package.dependencies] click = ">=7.0" h11 = ">=0.8" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] @@ -2119,5 +2083,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "48936a313a55194cc09311246fbfaeb79219b42db0cbbc12966f08757d48130e" +python-versions = "^3.11" +content-hash = "ad46598280e2d1bda9c28e899ce1854c6746033f250f5fc5a3528ba10001318a" diff --git a/pyproject.toml b/pyproject.toml index fa621c1c..016f1e80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" pendulum = "2.1.2" -ruff = "^0.5.7" +ruff = "^0.6.1" [tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 89ffe496..86c548c0 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -362,19 +362,15 @@ def parent_alias(cls, alias: str) -> str: last_delimiter = alias.rfind(".") if last_delimiter == -1: return "" - else: - return alias[:last_delimiter] + return alias[:last_delimiter] def parent_node(self, alias: str) -> Optional[ShNode]: parent_alias = self.parent_alias(alias) if not parent_alias: return None - else: - if parent_alias not in self.nodes: - raise DataClassLoadingError( - f"{alias} is missing parent {parent_alias}!" - ) - return self.node(parent_alias) + if parent_alias not in self.nodes: + raise DataClassLoadingError(f"{alias} is missing parent {parent_alias}!") + return self.node(parent_alias) def descendants(self, alias: str) -> List[ShNode]: return list(filter(lambda x: x.alias.startswith(alias), self.nodes.values())) diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index 46ad230e..6044ec9d 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -1,6 +1,5 @@ from gwproto import Decoders, MQTTCodec, create_message_payload_discriminator from gwproto.messages import GtDispatchBoolean_Maker, GtShCliAtnCmd_Maker - from tests.dummy_decoders import PARENT ChildMessageDecoder = create_message_payload_discriminator( diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index 30ddaa98..efc99519 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -6,7 +6,6 @@ ) from gwproto.gs import GsPwr_Maker from gwproto.messages import GtShStatus_Maker, SnapshotSpaceheat_Maker - from tests.dummy_decoders import CHILD ParentMessageDecoder = create_message_payload_discriminator( diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 7eb8151d..82348d26 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -3,6 +3,8 @@ import uuid from pathlib import Path +from pydantic import ValidationError + from gwproto import Message from gwproto.messages import ( Ack, @@ -26,8 +28,6 @@ SnapshotSpaceheatEvent, StartupEvent, ) -from pydantic import ValidationError - from tests.dummy_decoders import CHILD, PARENT from tests.dummy_decoders.child.codec import ChildMQTTCodec from tests.dummy_decoders.parent.codec import ParentMQTTCodec diff --git a/tests/test_gs_dispatch.py b/tests/test_gs_dispatch.py index 3146bddb..83678d9b 100644 --- a/tests/test_gs_dispatch.py +++ b/tests/test_gs_dispatch.py @@ -1,4 +1,5 @@ import pytest + from gwproto.errors import SchemaError from gwproto.messages import GsDispatch_Maker as Maker diff --git a/tests/test_gs_pwr.py b/tests/test_gs_pwr.py index 92321058..2cd2dcc4 100644 --- a/tests/test_gs_pwr.py +++ b/tests/test_gs_pwr.py @@ -1,4 +1,5 @@ import pytest + from gwproto.errors import SchemaError from gwproto.messages import GsPwr_Maker as Maker diff --git a/tests/test_message.py b/tests/test_message.py index a57700c9..3ba30b3b 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -2,9 +2,10 @@ from typing import Literal import pytest +from pydantic import BaseModel, ValidationError + from gwproto import Header, Message, as_enum from gwproto.message import PAYLOAD_TYPE_FIELDS -from pydantic import BaseModel, ValidationError class E(enum.Enum): diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 27f7a857..1e95bad6 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -4,7 +4,6 @@ from gwproto.data_classes.sh_node import ShNode from gwproto.types import ElectricMeterCacGt_Maker, SpaceheatNodeGt_Maker from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker - from tests.utils import flush_all diff --git a/tests/test_topic.py b/tests/test_topic.py index bd07b82c..ba09452c 100644 --- a/tests/test_topic.py +++ b/tests/test_topic.py @@ -1,4 +1,5 @@ import pytest + from gwproto import DecodedMQTTTopic, MQTTTopic diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index a22216ba..75841007 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import ComponentAttributeClassGt_Maker as Maker -from pydantic import ValidationError def test_component_attribute_class_gt_generated() -> None: diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index e6c73c2e..97aeee1b 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import ComponentGt_Maker as Maker -from pydantic import ValidationError def test_component_gt_generated() -> None: diff --git a/tests/types/test_data_channel.py b/tests/types/test_data_channel.py index e1eadd17..867de68d 100644 --- a/tests/types/test_data_channel.py +++ b/tests/types/test_data_channel.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import DataChannel_Maker as Maker -from pydantic import ValidationError def test_data_channel_generated() -> None: diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py index 0f7dfb77..7b34cc3f 100644 --- a/tests/types/test_egauge_io.py +++ b/tests/types/test_egauge_io.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import EgaugeIo_Maker as Maker -from pydantic import ValidationError def test_egauge_io_generated() -> None: diff --git a/tests/types/test_egauge_register_config.py b/tests/types/test_egauge_register_config.py index 96f710b8..39237ccb 100644 --- a/tests/types/test_egauge_register_config.py +++ b/tests/types/test_egauge_register_config.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import EgaugeRegisterConfig_Maker as Maker -from pydantic import ValidationError def test_egauge_register_config_generated() -> None: diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index b95f4777..b267619b 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import LocalCommInterface, MakeModel from gwproto.errors import SchemaError from gwproto.types import ElectricMeterCacGt_Maker as Maker -from pydantic import ValidationError def test_electric_meter_cac_gt_generated() -> None: diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index db802393..397c0cf2 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -3,11 +3,12 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types.electric_meter_component_gt import ( ElectricMeterComponentGt_Maker as Maker, ) -from pydantic import ValidationError def test_electric_meter_component_gt_generated() -> None: diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py index c98a7e68..cd659806 100644 --- a/tests/types/test_gt_dispatch_boolean.py +++ b/tests/types/test_gt_dispatch_boolean.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtDispatchBoolean_Maker as Maker -from pydantic import ValidationError def test_gt_dispatch_boolean_generated() -> None: diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py index 072df8f7..2371adce 100644 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ b/tests/types/test_gt_dispatch_boolean_local.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtDispatchBooleanLocal_Maker as Maker -from pydantic import ValidationError def test_gt_dispatch_boolean_local_generated() -> None: diff --git a/tests/types/test_gt_driver_booleanactuator_cmd.py b/tests/types/test_gt_driver_booleanactuator_cmd.py index 07693ca2..8568830e 100644 --- a/tests/types/test_gt_driver_booleanactuator_cmd.py +++ b/tests/types/test_gt_driver_booleanactuator_cmd.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtDriverBooleanactuatorCmd_Maker as Maker -from pydantic import ValidationError def test_gt_driver_booleanactuator_cmd_generated() -> None: diff --git a/tests/types/test_gt_sh_booleanactuator_cmd_status.py b/tests/types/test_gt_sh_booleanactuator_cmd_status.py index a5aa7d8a..50a883fe 100644 --- a/tests/types/test_gt_sh_booleanactuator_cmd_status.py +++ b/tests/types/test_gt_sh_booleanactuator_cmd_status.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtShBooleanactuatorCmdStatus_Maker as Maker -from pydantic import ValidationError def test_gt_sh_booleanactuator_cmd_status_generated() -> None: diff --git a/tests/types/test_gt_sh_cli_atn_cmd.py b/tests/types/test_gt_sh_cli_atn_cmd.py index 7a423d5c..47ac29b5 100644 --- a/tests/types/test_gt_sh_cli_atn_cmd.py +++ b/tests/types/test_gt_sh_cli_atn_cmd.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtShCliAtnCmd_Maker as Maker -from pydantic import ValidationError def test_gt_sh_cli_atn_cmd_generated() -> None: diff --git a/tests/types/test_gt_sh_multipurpose_telemetry_status.py b/tests/types/test_gt_sh_multipurpose_telemetry_status.py index 4f2fa6e7..1645b626 100644 --- a/tests/types/test_gt_sh_multipurpose_telemetry_status.py +++ b/tests/types/test_gt_sh_multipurpose_telemetry_status.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtShMultipurposeTelemetryStatus_Maker as Maker -from pydantic import ValidationError def test_gt_sh_multipurpose_telemetry_status_generated() -> None: diff --git a/tests/types/test_gt_sh_simple_telemetry_status.py b/tests/types/test_gt_sh_simple_telemetry_status.py index ee94a6d2..3d802472 100644 --- a/tests/types/test_gt_sh_simple_telemetry_status.py +++ b/tests/types/test_gt_sh_simple_telemetry_status.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtShSimpleTelemetryStatus_Maker as Maker -from pydantic import ValidationError def test_gt_sh_simple_telemetry_status_generated() -> None: diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index 6c4efdc2..c4ba62f4 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtShStatus_Maker as Maker -from pydantic import ValidationError def test_gt_sh_status_generated() -> None: diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index adbdd693..8419ee69 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import GtShTelemetryFromMultipurposeSensor_Maker as Maker -from pydantic import ValidationError def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: diff --git a/tests/types/test_gt_telemetry.py b/tests/types/test_gt_telemetry.py index ff21d985..729793f1 100644 --- a/tests/types/test_gt_telemetry.py +++ b/tests/types/test_gt_telemetry.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import TelemetryName from gwproto.errors import SchemaError from gwproto.types import GtTelemetry_Maker as Maker -from pydantic import ValidationError def test_gt_telemetry_generated() -> None: diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index 6489052a..1d75a83c 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import HeartbeatB_Maker as Maker -from pydantic import ValidationError def test_heartbeat_b_generated() -> None: diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index 167404d1..740caa30 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -1,6 +1,7 @@ """Test HubitatGt""" import yarl + from gwproto.types.hubitat_gt import HubitatGt diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index c6b52697..59c1a65f 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import MakeModel, Unit from gwproto.errors import SchemaError from gwproto.types import MultipurposeSensorCacGt_Maker as Maker -from pydantic import ValidationError def test_multipurpose_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index aa701dff..92308e0f 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -3,11 +3,12 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types.multipurpose_sensor_component_gt import ( MultipurposeSensorComponentGt_Maker as Maker, ) -from pydantic import ValidationError def test_multipurpose_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index 3e06c271..43f165a5 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import PipeFlowSensorCacGt_Maker as Maker -from pydantic import ValidationError def test_pipe_flow_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index ad298ff5..258ef71c 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import PipeFlowSensorComponentGt_Maker as Maker -from pydantic import ValidationError def test_pipe_flow_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_power_watts.py b/tests/types/test_power_watts.py index 6a66f7b4..09e18d94 100644 --- a/tests/types/test_power_watts.py +++ b/tests/types/test_power_watts.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import PowerWatts_Maker as Maker -from pydantic import ValidationError def test_power_watts_generated() -> None: diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index a2271cc6..8049a5a9 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import RelayCacGt_Maker as Maker -from pydantic import ValidationError def test_relay_cac_gt_generated() -> None: diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index 7f5e3a55..0e469f3e 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import RelayComponentGt_Maker as Maker -from pydantic import ValidationError def test_relay_component_gt_generated() -> None: diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 44dd46b2..142fb1b7 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import MakeModel from gwproto.errors import SchemaError from gwproto.types import ResistiveHeaterCacGt_Maker as Maker -from pydantic import ValidationError def test_resistive_heater_cac_gt_generated() -> None: diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index ef0a1e57..fc968072 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import ResistiveHeaterComponentGt_Maker as Maker -from pydantic import ValidationError def test_resistive_heater_component_gt_generated() -> None: diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index 6b37808b..b55976fb 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import MakeModel, TelemetryName, Unit from gwproto.errors import SchemaError from gwproto.types import SimpleTempSensorCacGt_Maker as Maker -from pydantic import ValidationError def test_simple_temp_sensor_cac_gt_generated() -> None: diff --git a/tests/types/test_simple_temp_sensor_component_gt.py b/tests/types/test_simple_temp_sensor_component_gt.py index bff43fb5..e4125a43 100644 --- a/tests/types/test_simple_temp_sensor_component_gt.py +++ b/tests/types/test_simple_temp_sensor_component_gt.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import SimpleTempSensorComponentGt_Maker as Maker -from pydantic import ValidationError def test_simple_temp_sensor_component_gt_generated() -> None: diff --git a/tests/types/test_snapshot_spaceheat.py b/tests/types/test_snapshot_spaceheat.py index 5723f7aa..70600d1a 100644 --- a/tests/types/test_snapshot_spaceheat.py +++ b/tests/types/test_snapshot_spaceheat.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import SnapshotSpaceheat_Maker as Maker -from pydantic import ValidationError def test_snapshot_spaceheat_generated() -> None: diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 4fade8e5..7e81d669 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import ActorClass, Role from gwproto.errors import SchemaError from gwproto.types import SpaceheatNodeGt_Maker as Maker -from pydantic import ValidationError def test_spaceheat_node_gt_generated() -> None: diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py index 2ffca99e..d373c077 100644 --- a/tests/types/test_ta_data_channels.py +++ b/tests/types/test_ta_data_channels.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import TaDataChannels_Maker as Maker -from pydantic import ValidationError def test_ta_data_channels_generated() -> None: diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index 9783633e..bcf9384c 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -3,10 +3,11 @@ import json import pytest +from pydantic import ValidationError + from gwproto.enums import TelemetryName, Unit from gwproto.errors import SchemaError from gwproto.types import TelemetryReportingConfig_Maker as Maker -from pydantic import ValidationError def test_telemetry_reporting_config_generated() -> None: diff --git a/tests/types/test_telemetry_snapshot_spaceheat.py b/tests/types/test_telemetry_snapshot_spaceheat.py index 18de6f73..9ecfd31d 100644 --- a/tests/types/test_telemetry_snapshot_spaceheat.py +++ b/tests/types/test_telemetry_snapshot_spaceheat.py @@ -3,9 +3,10 @@ import json import pytest +from pydantic import ValidationError + from gwproto.errors import SchemaError from gwproto.types import TelemetrySnapshotSpaceheat_Maker as Maker -from pydantic import ValidationError def test_telemetry_snapshot_spaceheat_generated() -> None: From ad4b3ff0c450b77e66bc7c54a0a3bc11af16b80c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 10:10:11 -0400 Subject: [PATCH 065/168] ruff: suppress warnings in property_format.py --- src/gwproto/property_format.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 8d9be4d2..53d3a615 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -84,7 +84,7 @@ def is_valid_asa_name(candidate: str) -> bool: """ try: candidate_len = len(candidate) - except: # noqa: E722 + except: # noqa return False return not candidate_len > 32 @@ -235,9 +235,7 @@ def check_is_positive_integer(candidate: int) -> None: def is_reasonable_unix_time_ms(candidate: int) -> bool: if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > candidate: # type: ignore[attr-defined] return False - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < candidate: # type: ignore[attr-defined] - return False - return True + return pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 >= candidate def check_is_reasonable_unix_time_ms(candidate: int) -> None: @@ -250,9 +248,7 @@ def check_is_reasonable_unix_time_ms(candidate: int) -> None: def is_reasonable_unix_time_s(candidate: int) -> bool: if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > candidate: # type: ignore[attr-defined] return False - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < candidate: # type: ignore[attr-defined] - return False - return True + return pendulum.parse("3000-01-01T00:00:00Z").int_timestamp >= candidate # type: ignore[attr-defined] def check_is_reasonable_unix_time_s(candidate: int) -> None: @@ -292,7 +288,7 @@ def check_is_short_integer(candidate: int) -> None: raise ValueError("short format requires (-32767 -1) <= number <= 32767") -def is_uuid_canonical_textual(candidate: str) -> bool: +def is_uuid_canonical_textual(candidate: str) -> bool: # noqa: PLR0911 try: x = candidate.split("-") except AttributeError: @@ -302,7 +298,7 @@ def is_uuid_canonical_textual(candidate: str) -> bool: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 return False if len(x[0]) != 8: return False @@ -325,7 +321,7 @@ def check_is_uuid_canonical_textual(candidate: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError("Words are not all hex") if len(x[0]) != 8: raise ValueError("Word 0 not of length 8") From 0970fe0399b9275ad2e9cbbf71f55587d6bc3efe Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 10:16:51 -0400 Subject: [PATCH 066/168] ruff: --preview --- noxfile.py | 3 +- src/gwproto/__init__.py | 26 ++++++------ .../data_classes/component_attribute_class.py | 6 +-- src/gwproto/messages/__init__.py | 40 +++++++++---------- src/gwproto/type_helpers/__init__.py | 2 +- src/gwproto/types/__init__.py | 10 ++--- 6 files changed, 40 insertions(+), 47 deletions(-) diff --git a/noxfile.py b/noxfile.py index eb5ee113..735c3286 100644 --- a/noxfile.py +++ b/noxfile.py @@ -98,7 +98,8 @@ def activate_virtualenv_in_precommit_hooks(session: Session) -> None: text = hook.read_text() if not any( - Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text + (Path("A") == Path("a") and bindir.lower() in text.lower()) + or bindir in text for bindir in bindirs ): continue diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index 0cf9f132..b2e11b14 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -29,34 +29,34 @@ from gwproto.topic import DecodedMQTTTopic, MQTTTopic __all__ = [ - "as_enum", "CacDecoder", "CallableDecoder", "ComponentDecoder", - "create_discriminator", - "create_message_payload_discriminator", - "decode_to_data_class", + "DecodedMQTTTopic", "Decoder", "DecoderItem", - "DecodedMQTTTopic", "Decoders", - "default_cac_decoder", - "default_component_decoder", - "get_pydantic_literal_type_name", "HardwareLayout", "Header", + "MQTTCodec", + "MQTTTopic", "MakerDecoder", "MakerExtractor", "Message", - "messages", "MessageDiscriminator", - "MQTTCodec", - "MQTTTopic", "OneDecoderExtractor", - "property_format", "PydanticDecoder", "PydanticTypeNameDecoder", - "pydantic_named_types", "SchemaError", "ShNode", + "as_enum", + "create_discriminator", + "create_message_payload_discriminator", + "decode_to_data_class", + "default_cac_decoder", + "default_component_decoder", + "get_pydantic_literal_type_name", + "messages", + "property_format", + "pydantic_named_types", ] diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 632261b8..0f5d5134 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -26,8 +26,4 @@ def __init__( self.display_name = display_name def __repr__(self) -> str: - return ( - self.display_name - if self.display_name - else self.component_attribute_class_id - ) + return self.display_name or self.component_attribute_class_id diff --git a/src/gwproto/messages/__init__.py b/src/gwproto/messages/__init__.py index c26d0e5c..75094d87 100644 --- a/src/gwproto/messages/__init__.py +++ b/src/gwproto/messages/__init__.py @@ -7,16 +7,20 @@ from .misc import * __all__ = [ - # gs + "Ack", + "AnyEvent", + "CommEvent", + "EventBase", + "EventMessage", + "EventT", "GsDispatch", "GsDispatch_Maker", "GsPwr", "GsPwr_Maker", - # gt "GtDispatchBoolean", - "GtDispatchBoolean_Maker", "GtDispatchBooleanLocal", "GtDispatchBooleanLocal_Maker", + "GtDispatchBoolean_Maker", "GtDriverBooleanactuatorCmd", "GtDriverBooleanactuatorCmd_Maker", "GtShBooleanactuatorCmdStatus", @@ -28,37 +32,29 @@ "GtShSimpleTelemetryStatus", "GtShSimpleTelemetryStatus_Maker", "GtShStatus", + "GtShStatusEvent", "GtShStatus_Maker", "GtShTelemetryFromMultipurposeSensor", "GtShTelemetryFromMultipurposeSensor_Maker", "GtTelemetry", "GtTelemetry_Maker", - "PowerWatts", - "PowerWatts_Maker", - "SnapshotSpaceheat", - "SnapshotSpaceheat_Maker", - "TelemetrySnapshotSpaceheat", - "TelemetrySnapshotSpaceheat_Maker", - # event - "AnyEvent", - "CommEvent", - "EventT", - "EventBase", - "EventMessage", - "GtShStatusEvent", "MQTTConnectEvent", "MQTTConnectFailedEvent", "MQTTDisconnectEvent", "MQTTFullySubscribedEvent", "PeerActiveEvent", - "Problems", + "Ping", + "PingMessage", + "PowerWatts", + "PowerWatts_Maker", "ProblemEvent", + "Problems", "ResponseTimeoutEvent", "ShutdownEvent", - "StartupEvent", + "SnapshotSpaceheat", "SnapshotSpaceheatEvent", - # misc - "Ack", - "Ping", - "PingMessage", + "SnapshotSpaceheat_Maker", + "StartupEvent", + "TelemetrySnapshotSpaceheat", + "TelemetrySnapshotSpaceheat_Maker", ] diff --git a/src/gwproto/type_helpers/__init__.py b/src/gwproto/type_helpers/__init__.py index 798e922c..75317637 100644 --- a/src/gwproto/type_helpers/__init__.py +++ b/src/gwproto/type_helpers/__init__.py @@ -22,8 +22,8 @@ "HubitatRESTResolutionSettings", "HubitatTankSettingsGt", "MakerAPIAttributeGt", - "RequestArgs", "RESTPollerSettings", + "RequestArgs", "SessionArgs", "URLArgs", "URLConfig", diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 158d95da..1098df5b 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -139,9 +139,9 @@ "FibaroSmartImplantComponentGt", "FibaroSmartImplantComponentGt_Maker", "GtDispatchBoolean", - "GtDispatchBoolean_Maker", "GtDispatchBooleanLocal", "GtDispatchBooleanLocal_Maker", + "GtDispatchBoolean_Maker", "GtDriverBooleanactuatorCmd", "GtDriverBooleanactuatorCmd_Maker", "GtShBooleanactuatorCmdStatus", @@ -182,6 +182,10 @@ "PipeFlowSensorComponentGt_Maker", "PowerWatts", "PowerWatts_Maker", + "RESTPollerCacGt", + "RESTPollerCacGt_Maker", + "RESTPollerComponentGt", + "RESTPollerComponentGt_Maker", "RelayCacGt", "RelayCacGt_Maker", "RelayComponentGt", @@ -190,10 +194,6 @@ "ResistiveHeaterCacGt_Maker", "ResistiveHeaterComponentGt", "ResistiveHeaterComponentGt_Maker", - "RESTPollerCacGt", - "RESTPollerCacGt_Maker", - "RESTPollerComponentGt", - "RESTPollerComponentGt_Maker", "SimpleTempSensorCacGt", "SimpleTempSensorCacGt_Maker", "SimpleTempSensorComponentGt", From 7404fc77836c9cacf77f440c17c0bc097834c888 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 10:27:29 -0400 Subject: [PATCH 067/168] ruff: --preview fixes --- src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py | 2 +- src/gwproto/data_classes/hardware_layout.py | 6 +++--- src/gwproto/types/gt_dispatch_boolean.py | 2 +- src/gwproto/types/gt_dispatch_boolean_local.py | 2 +- src/gwproto/types/gt_driver_booleanactuator_cmd.py | 2 +- tests/data_classes/test_electric_meter_cac.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index 8f6578c6..73586341 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -34,7 +34,7 @@ def __init__( SimpleTempSensorCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - if self.temp_unit not in [Unit.Celcius, Unit.Fahrenheit, Unit.Unitless]: + if self.temp_unit not in {Unit.Celcius, Unit.Fahrenheit, Unit.Unitless}: raise Exception( "TempSensorCac units must be Fahrenheit, Celsius or Unitless" ) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 86c548c0..a54fb253 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -502,7 +502,7 @@ def my_simple_sensors(self) -> List[ShNode]: filter( lambda x: ( x.actor_class - in (ActorClass.SimpleSensor, ActorClass.BooleanActuator) + in {ActorClass.SimpleSensor, ActorClass.BooleanActuator} ), all_nodes, ) @@ -515,12 +515,12 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: lambda x: ( ( x.actor_class - in ( + in { ActorClass.MultipurposeSensor, ActorClass.HubitatTankModule, ActorClass.HubitatPoller, ActorClass.HoneywellThermostat, - ) + } ) and hasattr(x.component, "config_list") ), diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 4d9d813a..facf0cd3 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -272,7 +272,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if v not in [0, 1]: + if v not in {0, 1}: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 53c3615e..4c7c14b0 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -237,7 +237,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if v not in [0, 1]: + if v not in {0, 1}: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index e56ce153..b82544cd 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -212,7 +212,7 @@ def check_is_bit(v: int) -> None: Raises: ValueError: if v is not 0 or 1 """ - if v not in [0, 1]: + if v not in {0, 1}: raise ValueError(f"<{v}> must be 0 or 1") diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 1be0dd26..9bc51006 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -23,4 +23,4 @@ def test_electric_meter_cac() -> None: assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] - assert dc.__repr__() == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" + assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" From 4f6b49449770676c05ff8a6f0a7c71cf52b63b5b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 10:38:22 -0400 Subject: [PATCH 068/168] ruff: disable PLR0915 in tests --- tests/types/test_electric_meter_cac_gt.py | 2 +- tests/types/test_electric_meter_component_gt.py | 2 +- tests/types/test_gt_dispatch_boolean.py | 2 +- tests/types/test_gt_dispatch_boolean_local.py | 2 +- tests/types/test_gt_sh_status.py | 2 +- tests/types/test_heartbeat_b.py | 2 +- tests/types/test_multipurpose_sensor_cac_gt.py | 2 +- tests/types/test_multipurpose_sensor_component_gt.py | 2 +- tests/types/test_pipe_flow_sensor_component_gt.py | 2 +- tests/types/test_relay_component_gt.py | 2 +- tests/types/test_resistive_heater_cac_gt.py | 2 +- tests/types/test_resistive_heater_component_gt.py | 2 +- tests/types/test_simple_temp_sensor_cac_gt.py | 2 +- tests/types/test_spaceheat_node_gt.py | 2 +- tests/types/test_ta_data_channels.py | 2 +- tests/types/test_telemetry_reporting_config.py | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index b267619b..6589d9dd 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import ElectricMeterCacGt_Maker as Maker -def test_electric_meter_cac_gt_generated() -> None: +def test_electric_meter_cac_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", "MakeModelGtEnumSymbol": "d300635e", diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 397c0cf2..f0b6d27e 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -11,7 +11,7 @@ ) -def test_electric_meter_component_gt_generated() -> None: +def test_electric_meter_component_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentId": "2dfb0cb6-6015-4273-b02b-bd446cc785d7", "ComponentAttributeClassId": "204832ef-0c88-408b-9640-264d2ee74914", diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py index cd659806..5b034ef4 100644 --- a/tests/types/test_gt_dispatch_boolean.py +++ b/tests/types/test_gt_dispatch_boolean.py @@ -9,7 +9,7 @@ from gwproto.types import GtDispatchBoolean_Maker as Maker -def test_gt_dispatch_boolean_generated() -> None: +def test_gt_dispatch_boolean_generated() -> None: # noqa: PLR0915 d = { "AboutNodeName": "a.elt1.relay", "ToGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py index 2371adce..9fbc266a 100644 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ b/tests/types/test_gt_dispatch_boolean_local.py @@ -9,7 +9,7 @@ from gwproto.types import GtDispatchBooleanLocal_Maker as Maker -def test_gt_dispatch_boolean_local_generated() -> None: +def test_gt_dispatch_boolean_local_generated() -> None: # noqa: PLR0915 d = { "RelayState": 1, "AboutNodeName": "a.elt1.relay", diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index c4ba62f4..0abd8157 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -9,7 +9,7 @@ from gwproto.types import GtShStatus_Maker as Maker -def test_gt_sh_status_generated() -> None: +def test_gt_sh_status_generated() -> None: # noqa: PLR0915 d = { "FromGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", "FromGNodeId": "0384ef21-648b-4455-b917-58a1172d7fc1", diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index 1d75a83c..19492a13 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -9,7 +9,7 @@ from gwproto.types import HeartbeatB_Maker as Maker -def test_heartbeat_b_generated() -> None: +def test_heartbeat_b_generated() -> None: # noqa: PLR0915 d = { "FromGNodeAlias": "d1.isone.ver.keene.holly", "FromGNodeInstanceId": "97eba574-bd20-45b5-bf82-9ba2f492d8f6", diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 59c1a65f..3337031b 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import MultipurposeSensorCacGt_Maker as Maker -def test_multipurpose_sensor_cac_gt_generated() -> None: +def test_multipurpose_sensor_cac_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", "MakeModelGtEnumSymbol": "09185ae3", diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index 92308e0f..3579b4ae 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -11,7 +11,7 @@ ) -def test_multipurpose_sensor_component_gt_generated() -> None: +def test_multipurpose_sensor_component_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index 258ef71c..d573bc57 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import PipeFlowSensorComponentGt_Maker as Maker -def test_pipe_flow_sensor_component_gt_generated() -> None: +def test_pipe_flow_sensor_component_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", "ComponentAttributeClassId": "13d916dc-8764-4b16-b85d-b8ead3e2fc80", diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index 0e469f3e..a4753e1e 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import RelayComponentGt_Maker as Maker -def test_relay_component_gt_generated() -> None: +def test_relay_component_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 142fb1b7..9b2b05bd 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import ResistiveHeaterCacGt_Maker as Maker -def test_resistive_heater_cac_gt_generated() -> None: +def test_resistive_heater_cac_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", "MakeModelGtEnumSymbol": "00000000", diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index fc968072..1da807dc 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import ResistiveHeaterComponentGt_Maker as Maker -def test_resistive_heater_component_gt_generated() -> None: +def test_resistive_heater_component_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index b55976fb..0866fd5b 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import SimpleTempSensorCacGt_Maker as Maker -def test_simple_temp_sensor_cac_gt_generated() -> None: +def test_simple_temp_sensor_cac_gt_generated() -> None: # noqa: PLR0915 d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", "MakeModelGtEnumSymbol": "acd93fb3", diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 7e81d669..4e01a4a3 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -10,7 +10,7 @@ from gwproto.types import SpaceheatNodeGt_Maker as Maker -def test_spaceheat_node_gt_generated() -> None: +def test_spaceheat_node_gt_generated() -> None: # noqa: PLR0915 d = { "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", "Alias": "a.elt1", diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py index d373c077..b52128d6 100644 --- a/tests/types/test_ta_data_channels.py +++ b/tests/types/test_ta_data_channels.py @@ -9,7 +9,7 @@ from gwproto.types import TaDataChannels_Maker as Maker -def test_ta_data_channels_generated() -> None: +def test_ta_data_channels_generated() -> None: # noqa: PLR0915 d = { "TerminalAssetGNodeAlias": "hw1.isone.me.versant.keene.oak.ta", "TerminalAssetGNodeId": "7e152072-c91b-49d2-9ebd-f4fe1b684d06", diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index bcf9384c..e247cb8b 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -10,7 +10,7 @@ from gwproto.types import TelemetryReportingConfig_Maker as Maker -def test_telemetry_reporting_config_generated() -> None: +def test_telemetry_reporting_config_generated() -> None: # noqa: PLR0915 d = { "TelemetryNameGtEnumSymbol": "af39eec9", "AboutNodeName": "a.elt1", From 90649c7d6540f43437e9c2b870a16e76c98e85dc Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 11:13:16 -0400 Subject: [PATCH 069/168] ruff suppress G004 for now --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 016f1e80..92b32e71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,6 +133,7 @@ ignore = [ "E501", "EM", "FA", # We only support Python >= 3.10, so we shouldn't need this + "G004", # Suppress for now; lots of them generated by code generation. "ISC001", "N801", "PGH004", # Suppress for now; lots of them generated by code generation. From 0ed387d42329963812593d7633e3b0e3ea614117 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 11:58:20 -0400 Subject: [PATCH 070/168] ruff: hardware_layout.py --- src/gwproto/data_classes/hardware_layout.py | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index a54fb253..fcca8e53 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -66,9 +66,9 @@ class LoadError: exception: Exception -def load_cacs( +def load_cacs( # noqa: C901 layout: dict[str, Any], - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, ) -> dict[str, Any]: @@ -88,7 +88,7 @@ def load_cacs( cacs[d["ComponentAttributeClassId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] d ) - except Exception as e: + except Exception as e: # noqa: PERF203 if raise_errors: raise errors.append(LoadError(type_name, d, e)) @@ -111,9 +111,9 @@ def load_cacs( return cacs -def load_components( +def load_components( # noqa: C901 layout: dict[Any, Any], - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, component_decoder: Optional[ComponentDecoder] = None, ) -> dict[Any, Any]: @@ -133,7 +133,7 @@ def load_components( components[d["ComponentId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] d ) - except Exception as e: + except Exception as e: # noqa: PERF203 if raise_errors: raise errors.append(LoadError(type_name, d, e)) @@ -156,7 +156,7 @@ def load_components( def load_nodes( layout: dict[Any, Any], - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, included_node_names: Optional[set[str]] = None, ) -> dict[Any, Any]: @@ -168,7 +168,7 @@ def load_nodes( node_name = d["Alias"] if included_node_names is None or node_name in included_node_names: nodes[node_name] = SpaceheatNodeGt_Maker.dict_to_dc(d) - except Exception as e: + except Exception as e: # noqa: PERF203 if raise_errors: raise errors.append(LoadError("ShNode", d, e)) @@ -178,7 +178,7 @@ def load_nodes( def resolve_links( nodes: dict[str, ShNode], components: dict[str, Component], - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, ) -> None: if errors is None: @@ -189,7 +189,7 @@ def resolve_links( if node.component_id is not None: component = components.get(node.component_id, None) if component is None: - raise DataClassLoadingError( + raise DataClassLoadingError( # noqa: TRY301 f"{node.alias} component {node.component_id} not loaded!" ) if isinstance(component, ComponentResolver): @@ -245,11 +245,11 @@ def clear_property_cache(self) -> None: self.__dict__.pop(cached_prop_name, None) @classmethod - def load( + def load( # noqa: PLR0913, PLR0917 cls, layout_path: Path | str, included_node_names: Optional[set[str]] = None, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, component_decoder: Optional[ComponentDecoder] = None, @@ -266,11 +266,11 @@ def load( ) @classmethod - def load_dict( + def load_dict( # noqa: PLR0913, PLR0917 cls, layout: dict[Any, Any], included_node_names: Optional[set[str]] = None, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, component_decoder: Optional[ComponentDecoder] = None, @@ -305,7 +305,7 @@ def load_dict( ) return HardwareLayout(layout, **load_args) - def node(self, alias: str, default: Any = None) -> ShNode: + def node(self, alias: str, default: Any = None) -> ShNode: # noqa: ANN401 return self.nodes.get(alias, default) def component(self, node_alias: str) -> Optional[Component]: @@ -326,7 +326,7 @@ def get_components_by_type(self, type_: Type[T]) -> list[T]: entries = self.components_by_type.get(type_, []) for i, entry in enumerate(entries): if not isinstance(entry, type_): - raise ValueError( + raise TypeError( f"ERROR. Entry {i + 1} in " f"HardwareLayout.components_by_typ[{type_}] " f"has the wrong type {type(entry)}" @@ -428,16 +428,14 @@ def all_nodes_in_agg_power_metering(self) -> List[ShNode]: @cached_property def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: - telemetry_tuples = [] - for config in self.power_meter_component.config_list: - telemetry_tuples.append( - TelemetryTuple( - AboutNode=self.node(config.AboutNodeName), - SensorNode=self.power_meter_node, - TelemetryName=config.TelemetryName, - ) + return [ + TelemetryTuple( + AboutNode=self.node(config.AboutNodeName), + SensorNode=self.power_meter_node, + TelemetryName=config.TelemetryName, ) - return telemetry_tuples + for config in self.power_meter_component.config_list + ] @cached_property def power_meter_node(self) -> ShNode: @@ -457,7 +455,7 @@ def power_meter_cac(self) -> ElectricMeterCac: if not isinstance( self.power_meter_component.component_attribute_class, ElectricMeterCac ): - raise ValueError( + raise TypeError( f"ERROR. power_meter_component cac {self.power_meter_component.component_attribute_class}" f" / {type(self.power_meter_component.component_attribute_class)} is not an ElectricMeterCac" ) @@ -485,7 +483,7 @@ def my_home_alone(self) -> ShNode: all_nodes = list(self.nodes.values()) home_alone_nodes = list(filter(lambda x: (x.role == Role.HomeAlone), all_nodes)) if len(home_alone_nodes) != 1: - raise Exception( + raise ValueError( "there should be a single SpaceheatNode with role HomeAlone" ) return home_alone_nodes[0] @@ -529,14 +527,16 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: ) telemetry_tuples = [] for node in multi_nodes: - for config in node.component.config_list: - telemetry_tuples.append( + telemetry_tuples.extend( + [ TelemetryTuple( AboutNode=self.node(config.AboutNodeName), SensorNode=node, TelemetryName=config.TelemetryName, ) - ) + for config in node.component.config_list + ] + ) return telemetry_tuples @cached_property From ca76901dfab0d483e94590d2c981689ff60ac21e Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 12:02:09 -0400 Subject: [PATCH 071/168] ruff: hardware_layout.py --- src/gwproto/data_classes/hardware_layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index fcca8e53..81e40a38 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -245,7 +245,7 @@ def clear_property_cache(self) -> None: self.__dict__.pop(cached_prop_name, None) @classmethod - def load( # noqa: PLR0913, PLR0917 + def load( # noqa: PLR0913, PLR0917, RUF100 cls, layout_path: Path | str, included_node_names: Optional[set[str]] = None, @@ -266,7 +266,7 @@ def load( # noqa: PLR0913, PLR0917 ) @classmethod - def load_dict( # noqa: PLR0913, PLR0917 + def load_dict( # noqa: PLR0913, PLR0917, RUF100 cls, layout: dict[Any, Any], included_node_names: Optional[set[str]] = None, From ba8ae7134571b5816ca83bf93f095a758b70a974 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 12:14:32 -0400 Subject: [PATCH 072/168] ruff: suppress PERF203 in generated validators --- src/gwproto/types/component_attribute_class_gt.py | 2 +- src/gwproto/types/component_gt.py | 2 +- src/gwproto/types/electric_meter_cac_gt.py | 2 +- src/gwproto/types/electric_meter_component_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 2 +- .../types/gt_sh_booleanactuator_cmd_status.py | 2 +- src/gwproto/types/gt_sh_cli_atn_cmd.py | 2 +- .../types/gt_sh_multipurpose_telemetry_status.py | 2 +- .../types/gt_sh_simple_telemetry_status.py | 15 ++++++--------- src/gwproto/types/gt_sh_status.py | 2 +- .../gt_sh_telemetry_from_multipurpose_sensor.py | 11 ++++------- src/gwproto/types/heartbeat_b.py | 2 +- src/gwproto/types/multipurpose_sensor_cac_gt.py | 2 +- .../types/multipurpose_sensor_component_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 2 +- .../types/pipe_flow_sensor_component_gt.py | 2 +- src/gwproto/types/relay_cac_gt.py | 2 +- src/gwproto/types/relay_component_gt.py | 2 +- src/gwproto/types/resistive_heater_cac_gt.py | 2 +- .../types/resistive_heater_component_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_cac_gt.py | 2 +- .../types/simple_temp_sensor_component_gt.py | 2 +- src/gwproto/types/snapshot_spaceheat.py | 2 +- src/gwproto/types/spaceheat_node_gt.py | 2 +- src/gwproto/types/ta_data_channels.py | 2 +- src/gwproto/types/telemetry_snapshot_spaceheat.py | 11 ++++------- 26 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index d0aa7eba..ed522bda 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -236,7 +236,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index ae10c389..e0c27041 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -269,7 +269,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 4f2e7a1e..5a0fb0f7 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -323,7 +323,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 38ead6c9..039e2d47 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -434,7 +434,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index facf0cd3..ee2fe3e5 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -347,7 +347,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index f3f7c694..d920f5ab 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -60,7 +60,7 @@ def _check_command_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: check_is_reasonable_unix_time_ms(elt) - except ValueError as e: + except ValueError as e: # noqa: PERF203 raise ValueError( f"CommandTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" ) diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index 069313de..acdef486 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -246,7 +246,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 0f2c67ce..9446c8b6 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -81,7 +81,7 @@ def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: check_is_reasonable_unix_time_ms(elt) - except ValueError as e: + except ValueError as e: # noqa: PERF203 raise ValueError( f"ReadTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" ) diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 3b4ad69d..4d502d79 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -2,8 +2,9 @@ import json import logging -from typing import Any, Dict, List, Literal +from typing import Any, Dict, List, Literal, Self +import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName @@ -68,14 +69,14 @@ def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: for elt in v: try: check_is_reasonable_unix_time_ms(elt) - except ValueError as e: + except ValueError as e: # noqa: PERF203 raise ValueError( f"ReadTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" ) return v @model_validator(mode="after") - def check_axiom_1(self) -> dict: + def check_axiom_1(self) -> Self: """ Axiom 1: ListLengthConsistency. ValueList and ReadTimeUnixMsList must have the same length. @@ -240,12 +241,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -270,8 +269,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index e2979c1f..21265166 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -398,7 +398,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 37abf5a8..a2f057e7 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self +import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName @@ -67,7 +68,7 @@ def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: for elt in v: try: check_is_left_right_dot(elt) - except ValueError as e: + except ValueError as e: # noqa: PERF203 raise ValueError( f"AboutNodeAliasList element {elt} failed LeftRightDot format validation: {e}" ) @@ -249,12 +250,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -279,8 +278,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 442735fd..3be02c51 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -354,7 +354,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index b6bb9263..fdddaead 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -328,7 +328,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index f89637dc..f840e863 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -345,7 +345,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 930db363..959ebe41 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -251,7 +251,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 635439a1..d1677259 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -295,7 +295,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 01914451..27e8b012 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -251,7 +251,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 1b2a2ef2..9c04079e 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -289,7 +289,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index a22cd657..0d177037 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -289,7 +289,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 663be4c8..a916d9cc 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -284,7 +284,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index d56f3bc1..e7970785 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -303,7 +303,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index 81000c43..ebb73ed6 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -278,7 +278,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index ef23cd8b..0a448165 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -247,7 +247,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 23f29e69..b8075312 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -402,7 +402,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index e45461a5..f4d82793 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -329,7 +329,7 @@ def check_is_uuid_canonical_textual(v: str) -> None: for hex_word in x: try: int(hex_word, 16) - except ValueError: + except ValueError: # noqa: PERF203 raise ValueError(f"Words of <{v}> are not all hex") if len(x[0]) != 8: raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index ca91264c..e71cd673 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self +import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName @@ -68,7 +69,7 @@ def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: for elt in v: try: check_is_left_right_dot(elt) - except ValueError as e: + except ValueError as e: # noqa: PERF203 raise ValueError( f"AboutNodeAliasList element {elt} failed LeftRightDot format validation: {e}" ) @@ -250,12 +251,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -280,8 +279,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] From c9bfb7b5d29d0bb4ea447c20fca503e1d1e9440b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 12:56:58 -0400 Subject: [PATCH 073/168] ruff: PLR0913, PLR0917, RUF100 --- src/gwproto/data_classes/cacs/electric_meter_cac.py | 2 +- src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py | 2 +- src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py | 2 +- src/gwproto/data_classes/components/electric_meter_component.py | 2 +- .../data_classes/components/multipurpose_sensor_component.py | 2 +- .../data_classes/components/pipe_flow_sensor_component.py | 2 +- src/gwproto/data_classes/components/relay_component.py | 2 +- .../data_classes/components/resistive_heater_component.py | 2 +- src/gwproto/data_classes/sh_node.py | 2 +- src/gwproto/types/egauge_register_config.py | 2 +- src/gwproto/types/electric_meter_cac_gt.py | 2 +- src/gwproto/types/electric_meter_component_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 2 +- src/gwproto/types/gt_sh_status.py | 2 +- src/gwproto/types/heartbeat_b.py | 2 +- src/gwproto/types/multipurpose_sensor_cac_gt.py | 2 +- src/gwproto/types/multipurpose_sensor_component_gt.py | 2 +- src/gwproto/types/pipe_flow_sensor_component_gt.py | 2 +- src/gwproto/types/relay_component_gt.py | 2 +- src/gwproto/types/resistive_heater_component_gt.py | 2 +- src/gwproto/types/simple_temp_sensor_cac_gt.py | 2 +- src/gwproto/types/spaceheat_node_gt.py | 2 +- src/gwproto/types/ta_data_channels.py | 2 +- src/gwproto/types/telemetry_reporting_config.py | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py index a013b2f3..f14662a5 100644 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ b/src/gwproto/data_classes/cacs/electric_meter_cac.py @@ -9,7 +9,7 @@ class ElectricMeterCac(ComponentAttributeClass): by_id: Dict[str, "ElectricMeterCac"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, make_model: MakeModel, diff --git a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py index 08b2c162..56ee4203 100644 --- a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py @@ -9,7 +9,7 @@ class MultipurposeSensorCac(ComponentAttributeClass): by_id: Dict[str, "MultipurposeSensorCac"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, exponent: int, diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index 73586341..b9997242 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -9,7 +9,7 @@ class SimpleTempSensorCac(ComponentAttributeClass): by_id: Dict[str, "SimpleTempSensorCac"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, exponent: int, diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index c10f02a0..88bbbdb8 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -11,7 +11,7 @@ class ElectricMeterComponent(Component): by_id: Dict[str, "ElectricMeterComponent"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index f327bf67..a751c6fd 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -11,7 +11,7 @@ class MultipurposeSensorComponent(Component): by_id: Dict[str, "MultipurposeSensorComponent"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 0926b687..0cc819f8 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -10,7 +10,7 @@ class PipeFlowSensorComponent(Component): by_id: Dict[str, "PipeFlowSensorComponent"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 6bd767ec..0545b4be 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -10,7 +10,7 @@ class RelayComponent(Component): by_id: Dict[str, "RelayComponent"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index ad15749a..1d960a41 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -10,7 +10,7 @@ class ResistiveHeaterComponent(Component): by_id: Dict[str, "ResistiveHeaterComponent"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 3c20b4b8..4272f731 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -18,7 +18,7 @@ class ShNode: by_id: Dict[str, "ShNode"] = {} - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, sh_node_id: str, alias: str, diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index ecd64dfd..4c55e186 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -121,7 +121,7 @@ class EgaugeRegisterConfig_Maker: type_name = "egauge.register.config" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, address: int, name: str, diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 5a0fb0f7..8ce32922 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -146,7 +146,7 @@ class ElectricMeterCacGt_Maker: type_name = "electric.meter.cac.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, make_model: EnumMakeModel, diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 039e2d47..4e068f5a 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -226,7 +226,7 @@ class ElectricMeterComponentGt_Maker: type_name = "electric.meter.component.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index ee2fe3e5..b351f39b 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -170,7 +170,7 @@ class GtDispatchBoolean_Maker: type_name = "gt.dispatch.boolean" version = "110" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, about_node_name: str, to_g_node_alias: str, diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 21265166..32fa8c6b 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -189,7 +189,7 @@ class GtShStatus_Maker: type_name = "gt.sh.status" version = "110" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, from_g_node_alias: str, from_g_node_id: str, diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 3be02c51..708ca23a 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -173,7 +173,7 @@ class HeartbeatB_Maker: type_name = "heartbeat.b" version = "001" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, from_g_node_alias: str, from_g_node_instance_id: str, diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index fdddaead..160eec82 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -159,7 +159,7 @@ class MultipurposeSensorCacGt_Maker: type_name = "multipurpose.sensor.cac.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, make_model: EnumMakeModel, diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index f840e863..8f255988 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -157,7 +157,7 @@ class MultipurposeSensorComponentGt_Maker: type_name = "multipurpose.sensor.component.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index d1677259..83f61338 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -149,7 +149,7 @@ class PipeFlowSensorComponentGt_Maker: type_name = "pipe.flow.sensor.component.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index 9c04079e..a495d551 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -149,7 +149,7 @@ class RelayComponentGt_Maker: type_name = "relay.component.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index a916d9cc..54768b37 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -146,7 +146,7 @@ class ResistiveHeaterComponentGt_Maker: type_name = "resistive.heater.component.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index e7970785..6af6864b 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -143,7 +143,7 @@ class SimpleTempSensorCacGt_Maker: type_name = "simple.temp.sensor.cac.gt" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_attribute_class_id: str, make_model: EnumMakeModel, diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index b8075312..952e2da3 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -191,7 +191,7 @@ class SpaceheatNodeGt_Maker: type_name = "spaceheat.node.gt" version = "100" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, sh_node_id: str, alias: str, diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index f4d82793..fbad773c 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -160,7 +160,7 @@ class TaDataChannels_Maker: type_name = "ta.data.channels" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, terminal_asset_g_node_alias: str, terminal_asset_g_node_id: str, diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 2298d53b..384f257f 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -154,7 +154,7 @@ class TelemetryReportingConfig_Maker: type_name = "telemetry.reporting.config" version = "000" - def __init__( + def __init__( # noqa: PLR0913, PLR0917, RUF100 self, telemetry_name: EnumTelemetryName, about_node_name: str, From a100b4d4744d2635f3fd1621c9f874244a755933 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 13:10:46 -0400 Subject: [PATCH 074/168] ruff: suppress RUF012 for 'by_id' dicts --- src/gwproto/data_classes/cacs/electric_meter_cac.py | 2 +- src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py | 2 +- src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py | 2 +- src/gwproto/data_classes/cacs/relay_cac.py | 2 +- src/gwproto/data_classes/cacs/resistive_heater_cac.py | 2 +- src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py | 2 +- src/gwproto/data_classes/component.py | 2 +- src/gwproto/data_classes/component_attribute_class.py | 2 +- src/gwproto/data_classes/components/relay_component.py | 2 +- src/gwproto/data_classes/sh_node.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py index f14662a5..e95732b8 100644 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ b/src/gwproto/data_classes/cacs/electric_meter_cac.py @@ -7,7 +7,7 @@ class ElectricMeterCac(ComponentAttributeClass): - by_id: Dict[str, "ElectricMeterCac"] = {} + by_id: Dict[str, "ElectricMeterCac"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py index 56ee4203..f8a38331 100644 --- a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py @@ -7,7 +7,7 @@ class MultipurposeSensorCac(ComponentAttributeClass): - by_id: Dict[str, "MultipurposeSensorCac"] = {} + by_id: Dict[str, "MultipurposeSensorCac"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py index 1045daf5..a5bfa63c 100644 --- a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py @@ -7,7 +7,7 @@ class PipeFlowSensorCac(ComponentAttributeClass): - by_id: Dict[str, "PipeFlowSensorCac"] = {} + by_id: Dict[str, "PipeFlowSensorCac"] = {} # noqa: RUF012 def __init__( self, diff --git a/src/gwproto/data_classes/cacs/relay_cac.py b/src/gwproto/data_classes/cacs/relay_cac.py index 0e1a3f33..cf6b68c7 100644 --- a/src/gwproto/data_classes/cacs/relay_cac.py +++ b/src/gwproto/data_classes/cacs/relay_cac.py @@ -7,7 +7,7 @@ class RelayCac(ComponentAttributeClass): - by_id: Dict[str, "RelayCac"] = {} + by_id: Dict[str, "RelayCac"] = {} # noqa: RUF012 def __init__( self, diff --git a/src/gwproto/data_classes/cacs/resistive_heater_cac.py b/src/gwproto/data_classes/cacs/resistive_heater_cac.py index 27a926f2..98173844 100644 --- a/src/gwproto/data_classes/cacs/resistive_heater_cac.py +++ b/src/gwproto/data_classes/cacs/resistive_heater_cac.py @@ -7,7 +7,7 @@ class ResistiveHeaterCac(ComponentAttributeClass): - by_id: Dict[str, "ResistiveHeaterCac"] = {} + by_id: Dict[str, "ResistiveHeaterCac"] = {} # noqa: RUF012 def __init__( self, diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index b9997242..7d7908e4 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -7,7 +7,7 @@ class SimpleTempSensorCac(ComponentAttributeClass): - by_id: Dict[str, "SimpleTempSensorCac"] = {} + by_id: Dict[str, "SimpleTempSensorCac"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 44d06dc1..5c9b19cf 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -8,7 +8,7 @@ class Component(ABC, StreamlinedSerializerMixin): - by_id: Dict[str, "Component"] = {} + by_id: Dict[str, "Component"] = {} # noqa: RUF012 base_props = [ "component_id", "display_name", diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 0f5d5134..04807793 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -7,7 +7,7 @@ class ComponentAttributeClass(ABC, StreamlinedSerializerMixin): - by_id: Dict[str, "ComponentAttributeClass"] = {} + by_id: Dict[str, "ComponentAttributeClass"] = {} # noqa: RUF012 base_props = ["component_attribute_class_id", "display_name"] diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 0545b4be..e0c39aac 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -8,7 +8,7 @@ class RelayComponent(Component): - by_id: Dict[str, "RelayComponent"] = {} + by_id: Dict[str, "RelayComponent"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 4272f731..ac0094a3 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -16,7 +16,7 @@ class ShNode: temperature data for the purposes of thermostatic control). """ - by_id: Dict[str, "ShNode"] = {} + by_id: Dict[str, "ShNode"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, From cb20e775be3f036bb07fab48465a64fe3358a5aa Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 13:31:32 -0400 Subject: [PATCH 075/168] ruff - data_classes --- src/gwproto/data_classes/cacs/relay_cac.py | 2 +- .../data_classes/cacs/simple_temp_sensor_cac.py | 2 +- src/gwproto/data_classes/component.py | 4 ++-- src/gwproto/data_classes/component_attribute_class.py | 6 +++--- .../components/electric_meter_component.py | 2 +- .../components/hubitat_poller_component.py | 2 +- .../data_classes/components/hubitat_tank_component.py | 3 ++- .../components/multipurpose_sensor_component.py | 2 +- .../components/pipe_flow_sensor_component.py | 2 +- .../data_classes/components/relay_component.py | 2 +- .../components/resistive_heater_component.py | 2 +- .../components/simple_temp_sensor_component.py | 2 +- src/gwproto/data_classes/mixin.py | 11 ++++------- 13 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/gwproto/data_classes/cacs/relay_cac.py b/src/gwproto/data_classes/cacs/relay_cac.py index cf6b68c7..390cd00c 100644 --- a/src/gwproto/data_classes/cacs/relay_cac.py +++ b/src/gwproto/data_classes/cacs/relay_cac.py @@ -30,5 +30,5 @@ def __repr__(self) -> str: return f"{self.make_model.value} {self.display_name}" @property - def telemetry_name(self): + def telemetry_name(self) -> TelemetryName: return TelemetryName.RelayState diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py index 7d7908e4..a77fbf0a 100644 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py @@ -35,7 +35,7 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 SimpleTempSensorCac.by_id[self.component_attribute_class_id] = self ComponentAttributeClass.by_id[self.component_attribute_class_id] = self if self.temp_unit not in {Unit.Celcius, Unit.Fahrenheit, Unit.Unitless}: - raise Exception( + raise ValueError( "TempSensorCac units must be Fahrenheit, Celsius or Unitless" ) diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 5c9b19cf..94c08320 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -9,14 +9,14 @@ class Component(ABC, StreamlinedSerializerMixin): by_id: Dict[str, "Component"] = {} # noqa: RUF012 - base_props = [ + base_props = [ # noqa: RUF012 "component_id", "display_name", "component_attribute_class_id", "hw_uid", ] - def __new__(cls, component_id, *args, **kwargs): + def __new__(cls, component_id, *args, **kwargs) -> "Component": # noqa: ANN001, ANN002, ANN003, ARG003 try: return cls.by_id[component_id] except KeyError: diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py index 04807793..f4624dae 100644 --- a/src/gwproto/data_classes/component_attribute_class.py +++ b/src/gwproto/data_classes/component_attribute_class.py @@ -1,7 +1,7 @@ """ComponentAttributeClass""" from abc import ABC -from typing import Dict, Optional +from typing import Any, Dict, Optional from gwproto.data_classes.mixin import StreamlinedSerializerMixin @@ -9,9 +9,9 @@ class ComponentAttributeClass(ABC, StreamlinedSerializerMixin): by_id: Dict[str, "ComponentAttributeClass"] = {} # noqa: RUF012 - base_props = ["component_attribute_class_id", "display_name"] + base_props = ["component_attribute_class_id", "display_name"] # noqa: RUF012 - def __new__(cls, component_attribute_class_id, *args, **kwargs): + def __new__(cls, component_attribute_class_id, *args: Any, **kwargs: Any): # noqa: ANN001, ANN204, ANN401, ARG003 try: return cls.by_id[component_attribute_class_id] except KeyError: diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index 88bbbdb8..cf426522 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -9,7 +9,7 @@ class ElectricMeterComponent(Component): - by_id: Dict[str, "ElectricMeterComponent"] = {} + by_id: Dict[str, "ElectricMeterComponent"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index 6a897a5b..31c43f64 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -44,7 +44,7 @@ def rest(self) -> RESTPollerSettings: def resolve( self, node_name: str, - nodes: dict[str, ShNode], + _nodes: dict[str, ShNode], components: dict[str, Component], ) -> None: if self._rest is not None: diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index c386ae71..cacc7700 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -22,7 +22,7 @@ class HubitatTankComponent(Component, ComponentResolver): sensor_supply_voltage: float default_poll_period_seconds: Optional[float] = None devices_gt: list[FibaroTempSensorSettingsGt] - devices: list[FibaroTempSensorSettings] = [] + devices: list[FibaroTempSensorSettings] web_listen_enabled: bool def __init__( @@ -42,6 +42,7 @@ def __init__( self.default_poll_period_seconds = tank_gt.default_poll_period_seconds self.devices_gt = list(tank_gt.devices) self.web_listen_enabled = tank_gt.web_listen_enabled + self.devices = [] super().__init__( display_name=display_name, component_id=component_id, diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index a751c6fd..806f9117 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -9,7 +9,7 @@ class MultipurposeSensorComponent(Component): - by_id: Dict[str, "MultipurposeSensorComponent"] = {} + by_id: Dict[str, "MultipurposeSensorComponent"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 0cc819f8..4604fe7d 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -8,7 +8,7 @@ class PipeFlowSensorComponent(Component): - by_id: Dict[str, "PipeFlowSensorComponent"] = {} + by_id: Dict[str, "PipeFlowSensorComponent"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index e0c39aac..3d30a4e4 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -14,7 +14,7 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 self, component_id: str, component_attribute_class_id: str, - normally_open: bool, + normally_open: bool, # noqa: FBT001 display_name: Optional[str] = None, gpio: Optional[int] = None, hw_uid: Optional[str] = None, diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index 1d960a41..fe282151 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -8,7 +8,7 @@ class ResistiveHeaterComponent(Component): - by_id: Dict[str, "ResistiveHeaterComponent"] = {} + by_id: Dict[str, "ResistiveHeaterComponent"] = {} # noqa: RUF012 def __init__( # noqa: PLR0913, PLR0917, RUF100 self, diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index 79b945bc..bcb38b6d 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -8,7 +8,7 @@ class SimpleTempSensorComponent(Component): - by_id: Dict[str, "SimpleTempSensorComponent"] = {} + by_id: Dict[str, "SimpleTempSensorComponent"] = {} # noqa: RUF012 def __init__( self, diff --git a/src/gwproto/data_classes/mixin.py b/src/gwproto/data_classes/mixin.py index 7443b7cc..a6eb1365 100644 --- a/src/gwproto/data_classes/mixin.py +++ b/src/gwproto/data_classes/mixin.py @@ -3,10 +3,7 @@ class StreamlinedSerializerMixin: @property - def streamlined_serialize(self): - output = {} - for key, value in self.__dict__.items(): - if value is not None: - output[key] = value - - return json.dumps(output) + def streamlined_serialize(self) -> str: + return json.dumps( + {key: value for key, value in self.__dict__.items() if value is not None} + ) From e840a8c36611f1b9f3187f02a733962d3f076fba Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 13:58:57 -0400 Subject: [PATCH 076/168] ruff: warnings suppression in tests --- tests/dummy_decoders/child/codec.py | 4 ++-- tests/dummy_decoders/parent/codec.py | 4 ++-- tests/test_gs_dispatch.py | 2 +- tests/test_gs_pwr.py | 2 +- tests/test_topic.py | 10 +++++----- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index 6044ec9d..a4813c35 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -22,6 +22,6 @@ def __init__(self) -> None: ) ) - def validate_source_alias(self, source_alias: str) -> None: + def validate_source_alias(self, source_alias: str) -> None: # noqa: PLR6301, RUF100 if source_alias != PARENT: - raise Exception(f"alias {source_alias} not my parent!") + raise ValueError(f"alias {source_alias} not my parent!") diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index efc99519..81f016ad 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -28,6 +28,6 @@ def __init__(self) -> None: ) ) - def validate_source_alias(self, source_alias: str) -> None: + def validate_source_alias(self, source_alias: str) -> None: # noqa: PLR6301, RUF100 if source_alias != CHILD: - raise Exception(f"alias {source_alias} not my child") + raise ValueError(f"alias {source_alias} not my child") diff --git a/tests/test_gs_dispatch.py b/tests/test_gs_dispatch.py index 83678d9b..972aa16f 100644 --- a/tests/test_gs_dispatch.py +++ b/tests/test_gs_dispatch.py @@ -7,7 +7,7 @@ def test_gs_dispatch() -> None: gw_tuple = Maker(relay_state=1).tuple - assert Maker.tuple_to_type(gw_tuple) == b"\x01\x00" # type: ignore + assert Maker.tuple_to_type(gw_tuple) == b"\x01\x00" assert Maker.type_to_tuple(b"\x01\x00") == gw_tuple with pytest.raises(SchemaError): diff --git a/tests/test_gs_pwr.py b/tests/test_gs_pwr.py index 2cd2dcc4..b068cb5b 100644 --- a/tests/test_gs_pwr.py +++ b/tests/test_gs_pwr.py @@ -7,7 +7,7 @@ def test_gs_pwr() -> None: gw_tuple = Maker(power=3200).tuple - assert Maker.tuple_to_type(gw_tuple) == b"\x80\x0c" # type: ignore + assert Maker.tuple_to_type(gw_tuple) == b"\x80\x0c" assert Maker.type_to_tuple(b"\x80\x0c") == gw_tuple with pytest.raises(SchemaError): diff --git a/tests/test_topic.py b/tests/test_topic.py index ba09452c..f6937cf3 100644 --- a/tests/test_topic.py +++ b/tests/test_topic.py @@ -11,7 +11,7 @@ def test_mqtt_topic_encode() -> None: def test_mqtt_topic_decode() -> None: - with pytest.raises(ValueError): + with pytest.raises(ValueError): # noqa: PT011 MQTTTopic.decode("") decoded = MQTTTopic.decode("foo/bar/baz") @@ -22,7 +22,7 @@ def test_mqtt_topic_decode() -> None: assert decoded == DecodedMQTTTopic( envelope_type="foo", src="bar", message_type="baz" ) - assert str(decoded) != "" + assert str(decoded) decoded = MQTTTopic.decode("foo-bar/baz-bla/bla") assert decoded.envelope_type == "foo.bar" @@ -35,14 +35,14 @@ def test_mqtt_topic_decode() -> None: decoded = MQTTTopic.decode("foo") assert decoded.envelope_type == "foo" - assert decoded.src == "" - assert decoded.message_type == "" + assert not decoded.src + assert not decoded.message_type assert decoded.remainder == [] decoded = MQTTTopic.decode("foo/bar") assert decoded.envelope_type == "foo" assert decoded.src == "bar" - assert decoded.message_type == "" + assert not decoded.message_type assert decoded.remainder == [] decoded = MQTTTopic.decode("foo/bar/baz/a/b/c") From b3fb295dcc83fb49f80e86de9688393ccff559bc Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 15:22:28 -0400 Subject: [PATCH 077/168] ruff: utils.py - deleted functions with warnings that were not called anywhere in the project --- src/gwproto/utils.py | 79 -------------------------------------------- 1 file changed, 79 deletions(-) diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index bbf00dd5..eb2c18c2 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -1,10 +1,4 @@ -import datetime -import json import re -import subprocess -from typing import Any - -import pytz snake_add_underscore_to_camel_pattern = re.compile(r"(? str: return "".join(x.capitalize() or "_" for x in word.split("_")) -def string_to_dict(payload_as_string: str) -> dict[str, Any]: - payload_as_dict = json.loads(payload_as_string) - if not isinstance(payload_as_dict, dict): - raise ValueError( - f"ERROR. json.loads(payload) produced {type(payload_as_dict)} not dict" - ) - return payload_as_dict - - -def log_style_utc_date(timestamp: float) -> str: - d = datetime.datetime.utcfromtimestamp(timestamp) - d = pytz.UTC.localize(d) - utc_date = d.strftime("%Y-%m-%d %H:%M:%S") - return utc_date + d.strftime(" %Z") - - -def log_style_utc_date_w_millis(timestamp: float) -> str: - d = datetime.datetime.utcfromtimestamp(timestamp) - d = pytz.UTC.localize(d) - millis = round(d.microsecond / 1000) - if millis == 1000: - d += datetime.timedelta(seconds=1) - millis = 0 - utc_date = d.strftime("%Y-%m-%d %H:%M:%S") - if 0 <= millis < 10: - millis_string = f".00{millis}" - elif 10 <= millis < 100: - millis_string = f".0{millis}" - else: - millis_string = f".{millis}" - return utc_date + millis_string + d.strftime(" %Z") - - -def screen_style_utc_date_w_millis(timestamp: float) -> str: - d = datetime.datetime.utcfromtimestamp(timestamp) - d = pytz.UTC.localize(d) - millis = round(d.microsecond / 1000) - utc_date = d.strftime("%M:%S") - if 0 <= millis < 10: - millis_string = f".00{millis}" - elif 10 <= millis < 100: - millis_string = f".0{millis}" - else: - millis_string = f".{millis}" - return utc_date + millis_string - - -def screen_style_utc_date_s(timestamp: float) -> str: - d = datetime.datetime.utcfromtimestamp(timestamp) - d = pytz.UTC.localize(d) - utc_date = d.strftime("%Y-%m-%d %H:%M:%S") - return utc_date + " UTC" - - -def get_git_short_commit() -> str: - return bytes.decode( - subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]) - ).split("\n")[0] - - def rld_alias(alias: str) -> str: return ".".join(reversed(alias.split("."))) -def all_equal(iterator: Any) -> bool: - iterator = iter(iterator) - try: - first = next(iterator) - except StopIteration: - return True - return all(first == x for x in iterator) - - -# def predicate_validator( -# field_name: str, predicate: Callable[[Any], bool], error_format: str = "", **kwargs -# ) -> classmethod: # type: ignore - MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") From 0c3a45f8680392517150ca9b679fc6ed426c632a Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 15:27:25 -0400 Subject: [PATCH 078/168] ruff: hubitat cac, component - remove Makers and clean up warnings --- src/gwproto/types/__init__.py | 5 +-- src/gwproto/types/hubitat_cac_gt.py | 42 -------------------- src/gwproto/types/hubitat_component_gt.py | 48 ++--------------------- 3 files changed, 4 insertions(+), 91 deletions(-) diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 1098df5b..4bdfa232 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -52,10 +52,9 @@ ) from gwproto.types.gt_telemetry import GtTelemetry, GtTelemetry_Maker from gwproto.types.heartbeat_b import HeartbeatB, HeartbeatB_Maker -from gwproto.types.hubitat_cac_gt import HubitatCacGt, HubitatCacGt_Maker +from gwproto.types.hubitat_cac_gt import HubitatCacGt from gwproto.types.hubitat_component_gt import ( HubitatComponentGt, - HubitatComponentGt_Maker, ) from gwproto.types.hubitat_poller_cac_gt import ( HubitatPollerCacGt, @@ -161,9 +160,7 @@ "HeartbeatB", "HeartbeatB_Maker", "HubitatCacGt", - "HubitatCacGt_Maker", "HubitatComponentGt", - "HubitatComponentGt_Maker", "HubitatPollerCacGt", "HubitatPollerCacGt_Maker", "HubitatPollerComponentGt", diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index 5b65d2f2..b03f14b7 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -1,4 +1,3 @@ -import json import typing from typing import Literal @@ -29,44 +28,3 @@ def to_data_class(self) -> HubitatCac: def __hash__(self) -> int: return hash((type(self), *tuple(self.__dict__.values()))) - - -class HubitatCacGt_Maker: - type_name: str = HubitatCacGt.model_fields["TypeName"].default - version = "000" - tuple: HubitatCacGt - - def __init__(self, cac: HubitatCac) -> None: - self.tuple = HubitatCacGt.from_data_class(cac) - - @classmethod - def tuple_to_type(cls, tpl: HubitatCacGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> HubitatCacGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, typing.Any]) -> HubitatCacGt: - return HubitatCacGt(**d) - - @classmethod - def tuple_to_dc(cls, t: HubitatCacGt) -> HubitatCac: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: HubitatCac) -> HubitatCacGt: - return HubitatCacGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> HubitatCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: HubitatCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[typing.Any, str]) -> HubitatCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index c8d85954..a43b3bd7 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -1,6 +1,5 @@ -import json import typing -from typing import Any, Literal, Optional +from typing import Literal, Optional import yarl @@ -57,7 +56,7 @@ def to_data_class(self) -> HubitatComponent: ) @classmethod - def make_stub(cls, component_id): + def make_stub(cls, component_id: str) -> "HubitatComponentGt": return HubitatComponentGt( ComponentId=component_id, ComponentAttributeClassId="00000000-0000-0000-0000-000000000000", @@ -80,7 +79,7 @@ def from_component_id( f"HubitatTankComponent.hubitat.CompnentId {component_id}" ) if not isinstance(hubitat_component, HubitatComponent): - raise ValueError( + raise TypeError( "ERROR. Referenced hubitat component has type " f"{type(hubitat_component)}; " "must be instance of HubitatComponent. " @@ -96,44 +95,3 @@ class HubitatRESTResolutionSettings: def __init__(self, component_gt: HubitatComponentGt) -> None: self.component_gt = component_gt self.maker_api_url_config = self.component_gt.maker_api_url_config() - - -class HubitatComponentGt_Maker: - type_name: str = HubitatComponentGt.model_fields["TypeName"].default - version = "000" - tuple: HubitatComponentGt - - def __init__(self, component: HubitatComponent) -> None: - self.tuple = HubitatComponentGt.from_data_class(component) - - @classmethod - def tuple_to_type(cls, tpl: HubitatComponentGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> HubitatComponentGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> HubitatComponentGt: - return HubitatComponentGt(**d) - - @classmethod - def tuple_to_dc(cls, t: HubitatComponentGt) -> HubitatComponent: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: HubitatComponent) -> HubitatComponentGt: - return HubitatComponentGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> HubitatComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: HubitatComponent) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> HubitatComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) From 1b6d5fa350be29fd6117ae354a9b604ac236938f Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 15:30:18 -0400 Subject: [PATCH 079/168] ruff: messages/event.py --- src/gwproto/messages/event.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gwproto/messages/event.py b/src/gwproto/messages/event.py index cc60d085..09471651 100644 --- a/src/gwproto/messages/event.py +++ b/src/gwproto/messages/event.py @@ -27,10 +27,8 @@ class AnyEvent(EventBase, extra="allow"): class EventMessage(Message[EventT], Generic[EventT]): - def __init__(self, **data: Any) -> None: - if "AckRequired" not in data: - data["AckRequired"] = True - super().__init__(**data) + def __init__(self, AckRequired: bool = True, **kwargs: Any) -> None: # noqa: ANN401, FBT001, FBT002, N803 + super().__init__(AckRequired=AckRequired, **kwargs) class StartupEvent(EventBase): @@ -55,7 +53,7 @@ class ProblemEvent(EventBase): @field_validator("ProblemType", mode="before") @classmethod - def problem_type_value(cls, v: Any) -> Optional[Problems]: + def problem_type_value(cls, v: Any) -> Optional[Problems]: # noqa: ANN401 return as_enum(v, Problems, Problems.error) From e03e8b22b2a9b947e0660b15f55b8d02b1efc6a6 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 20 Aug 2024 18:43:48 -0400 Subject: [PATCH 080/168] ruff: clean up or suppress remaining errors - 'ruff check' passes --- noxfile.py | 6 ++-- src/gwproto/enums/actor_class.py | 6 ++-- src/gwproto/enums/local_comm_interface.py | 8 ++--- src/gwproto/enums/make_model.py | 6 ++-- src/gwproto/enums/role.py | 6 ++-- src/gwproto/enums/telemetry_name.py | 6 ++-- src/gwproto/enums/unit.py | 6 ++-- src/gwproto/gs/gs_dispatch_maker.py | 6 ++-- src/gwproto/gs/gs_pwr_maker.py | 6 ++-- src/gwproto/types/data_channel.py | 8 ++--- src/gwproto/types/electric_meter_cac_gt.py | 14 ++++----- .../types/electric_meter_component_gt.py | 16 ++++------ src/gwproto/types/gt_dispatch_boolean.py | 11 +++---- .../types/gt_dispatch_boolean_local.py | 11 +++---- .../types/gt_driver_booleanactuator_cmd.py | 11 +++---- .../types/gt_sh_booleanactuator_cmd_status.py | 9 ++---- src/gwproto/types/gt_sh_cli_atn_cmd.py | 10 +++---- .../gt_sh_multipurpose_telemetry_status.py | 9 ++---- src/gwproto/types/gt_sh_status.py | 30 +++++++------------ ...t_sh_telemetry_from_multipurpose_sensor.py | 9 +++--- src/gwproto/types/heartbeat_b.py | 21 +++++++------ src/gwproto/types/hubitat_tank_gt.py | 6 ++-- .../types/multipurpose_sensor_cac_gt.py | 14 ++++----- .../types/multipurpose_sensor_component_gt.py | 19 +++++------- src/gwproto/types/relay_component_gt.py | 8 ++--- src/gwproto/types/rest_poller_component_gt.py | 4 +-- src/gwproto/types/rest_poller_gt.py | 12 ++++---- .../types/simple_temp_sensor_component_gt.py | 4 +-- src/gwproto/types/snapshot_spaceheat.py | 8 ++--- src/gwproto/types/spaceheat_node_gt.py | 12 ++++---- src/gwproto/types/ta_data_channels.py | 16 ++++------ .../types/telemetry_reporting_config.py | 10 +++---- .../types/telemetry_snapshot_spaceheat.py | 9 +++--- 33 files changed, 143 insertions(+), 194 deletions(-) diff --git a/noxfile.py b/noxfile.py index 735c3286..602f2ef4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -7,12 +7,12 @@ from pathlib import Path from textwrap import dedent -import nox # type: ignore +import nox # noqa: ALL try: - from nox_poetry import ( # type: ignore + from nox_poetry import ( Session, - session, # type: ignore + session, ) except ImportError: message = f"""\ diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 83c6ffde..0eac088d 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -86,7 +86,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -106,7 +106,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -139,7 +139,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "NoActor". """ if symbol not in symbol_to_value: - return cls.default().value + return str(cls.default().value) return symbol_to_value[symbol] @classmethod diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 7a22cbed..950b9d99 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -49,7 +49,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -69,7 +69,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -102,7 +102,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "Unknown". """ if symbol not in symbol_to_value: - return cls.default().value + return str(cls.default().value) return symbol_to_value[symbol] @classmethod @@ -120,7 +120,7 @@ def value_to_symbol(cls, value: str) -> str: symbol of "00000000". """ if value not in value_to_symbol: - return value_to_symbol[cls.default().value] + return value_to_symbol[str(cls.default().value)] return value_to_symbol[value] @classmethod diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index 8f1af216..dbb186a1 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -94,7 +94,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -114,7 +114,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -147,7 +147,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "UnknownMake__UnknownModel". """ if symbol not in symbol_to_value: - return cls.default().value + return cls.default().value # noqa: ALL return symbol_to_value[symbol] @classmethod diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 1a832ba0..31f9f811 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -82,7 +82,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -102,7 +102,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -135,7 +135,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "Unknown". """ if symbol not in symbol_to_value: - return cls.default().value + return cls.default().value # noqa: ALL return symbol_to_value[symbol] @classmethod diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index a19cac56..0c38dcff 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -71,7 +71,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -91,7 +91,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -124,7 +124,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "Unknown". """ if symbol not in symbol_to_value: - return cls.default().value + return cls.default().value # noqa: ALL return symbol_to_value[symbol] @classmethod diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index fc273aba..7dc04a1e 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -53,7 +53,7 @@ def values(cls) -> List[str]: """ Returns enum choices """ - return [elt.value for elt in cls] + return [elt.value for elt in cls] # noqa: ALL @classmethod def version(cls, value: str) -> str: @@ -73,7 +73,7 @@ def version(cls, value: str) -> str: str: The earliest version of the enum containing value. """ if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") + raise ValueError("This method applies to strings, not enums") # noqa: TRY004 if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -106,7 +106,7 @@ def symbol_to_value(cls, symbol: str) -> str: a later version of this enum, returns the default value of "Unknown". """ if symbol not in symbol_to_value: - return cls.default().value + return cls.default().value # noqa: ALL return symbol_to_value[symbol] @classmethod diff --git a/src/gwproto/gs/gs_dispatch_maker.py b/src/gwproto/gs/gs_dispatch_maker.py index 256e3ddd..4b08b18a 100644 --- a/src/gwproto/gs/gs_dispatch_maker.py +++ b/src/gwproto/gs/gs_dispatch_maker.py @@ -8,8 +8,8 @@ class GsDispatch_Maker: type_name = "d" - def __init__(self, relay_state) -> None: - tpl = GsDispatch(RelayState=relay_state) + def __init__(self, relay_state: int) -> None: + tpl = GsDispatch(RelayState=relay_state) # noqa: ALL tpl.check_for_errors() self.tuple = tpl @@ -21,6 +21,6 @@ def tuple_to_type(cls, tpl: GsDispatch) -> bytes: @classmethod def type_to_tuple(cls, b: bytes) -> GsDispatch: (relay_state,) = struct.unpack(" None: - tpl = GsPwr(Power=power) + def __init__(self, power: int) -> None: + tpl = GsPwr(Power=power) # noqa: ALL tpl.check_for_errors() self.tuple = tpl @@ -21,6 +21,6 @@ def tuple_to_type(cls, tpl: GsPwr) -> bytes: @classmethod def type_to_tuple(cls, b: bytes) -> GsPwr: (power,) = struct.unpack(" None: Raises: ValueError: If the provided string is not in SpaceheatName format. """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 8ce32922..0cdcef45 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -106,10 +106,10 @@ def as_dict(self) -> Dict[str, Any]: del d["MakeModel"] d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) del d["TelemetryNameList"] - telemetry_name_list = [] - for elt in self.TelemetryNameList: - telemetry_name_list.append(TelemetryName.value_to_symbol(elt.value)) - d["TelemetryNameList"] = telemetry_name_list + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(str(elt.value)) + for elt in self.TelemetryNameList + ] del d["Interface"] d["InterfaceGtEnumSymbol"] = LocalCommInterface.value_to_symbol(self.Interface) return d @@ -187,7 +187,7 @@ def type_to_tuple(cls, t: bytes) -> ElectricMeterCacGt: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: + def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: # noqa: C901 """ Deserialize a dictionary representation of a electric.meter.cac.gt.000 message object into a ElectricMeterCacGt python object for internal use. @@ -273,11 +273,11 @@ def dc_to_tuple(cls, dc: ElectricMeterCac) -> ElectricMeterCacGt: @classmethod def type_to_dc(cls, t: str) -> ElectricMeterCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) @classmethod def dc_to_type(cls, dc: ElectricMeterCac) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> ElectricMeterCac: diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 4e068f5a..bf8a6558 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -183,15 +183,9 @@ def as_dict(self) -> Dict[str, Any]: if value is not None } # Recursively calling as_dict() - config_list = [] - for elt in self.ConfigList: - config_list.append(elt.as_dict()) - d["ConfigList"] = config_list + d["ConfigList"] = [elt.as_dict() for elt in self.ConfigList] # Recursively calling as_dict() - egauge_io_list = [] - for elt in self.EgaugeIoList: - egauge_io_list.append(elt.as_dict()) - d["EgaugeIoList"] = egauge_io_list + d["EgaugeIoList"] = [elt.as_dict() for elt in self.EgaugeIoList] return d def as_type(self) -> bytes: @@ -269,7 +263,7 @@ def type_to_tuple(cls, t: bytes) -> ElectricMeterComponentGt: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: + def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: # noqa: C901, PLR0912 """ Deserialize a dictionary representation of a electric.meter.component.gt.000 message object into a ElectricMeterComponentGt python object for internal use. @@ -366,11 +360,11 @@ def dc_to_tuple(cls, dc: ElectricMeterComponent) -> ElectricMeterComponentGt: @classmethod def type_to_dc(cls, t: str) -> ElectricMeterComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) @classmethod def dc_to_type(cls, dc: ElectricMeterComponent) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> ElectricMeterComponent: diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index b351f39b..0c9c71c9 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -288,12 +289,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -318,8 +317,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 4c7c14b0..1446cee7 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -253,12 +254,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -283,8 +282,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index b82544cd..7bf8db7d 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -228,12 +229,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -258,8 +257,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index d920f5ab..a764d7ca 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -209,12 +210,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -239,8 +238,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index acdef486..b80d100b 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -122,7 +122,7 @@ class GtShCliAtnCmd_Maker: def __init__( self, from_g_node_alias: str, - send_snapshot: bool, + send_snapshot: bool, # noqa: FBT001 from_g_node_id: str, ) -> None: self.tuple = GtShCliAtnCmd( @@ -206,12 +206,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 9446c8b6..befe37d8 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal, Self +import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName @@ -257,12 +258,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -287,8 +286,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 32fa8c6b..9e6c431e 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -141,20 +142,15 @@ def as_dict(self) -> Dict[str, Any]: if value is not None } # Recursively calling as_dict() - simple_telemetry_list = [] - for elt in self.SimpleTelemetryList: - simple_telemetry_list.append(elt.as_dict()) - d["SimpleTelemetryList"] = simple_telemetry_list + d["SimpleTelemetryList"] = [elt.as_dict() for elt in self.SimpleTelemetryList] # Recursively calling as_dict() - multipurpose_telemetry_list = [] - for elt in self.MultipurposeTelemetryList: - multipurpose_telemetry_list.append(elt.as_dict()) - d["MultipurposeTelemetryList"] = multipurpose_telemetry_list + d["MultipurposeTelemetryList"] = [ + elt.as_dict() for elt in self.MultipurposeTelemetryList + ] # Recursively calling as_dict() - booleanactuator_cmd_list = [] - for elt in self.BooleanactuatorCmdList: - booleanactuator_cmd_list.append(elt.as_dict()) - d["BooleanactuatorCmdList"] = booleanactuator_cmd_list + d["BooleanactuatorCmdList"] = [ + elt.as_dict() for elt in self.BooleanactuatorCmdList + ] return d def as_type(self) -> bytes: @@ -234,7 +230,7 @@ def type_to_tuple(cls, t: bytes) -> GtShStatus: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: + def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: # noqa: C901, PLR0912, PLR0915 """ Deserialize a dictionary representation of a gt.sh.status.110 message object into a GtShStatus python object for internal use. @@ -339,12 +335,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -369,8 +363,6 @@ def check_is_reasonable_unix_time_s(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeS format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index a2f057e7..a93c73e8 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -113,11 +113,10 @@ def as_dict(self) -> Dict[str, Any]: ).items() if value is not None } - del d["TelemetryNameList"] - telemetry_name_list = [] - for elt in self.TelemetryNameList: - telemetry_name_list.append(TelemetryName.value_to_symbol(elt.value)) - d["TelemetryNameList"] = telemetry_name_list + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(str(elt.value)) + for elt in self.TelemetryNameList + ] return d def as_type(self) -> bytes: diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 708ca23a..5f87a375 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -92,7 +93,9 @@ def _check_your_last_hex(cls, v: str) -> str: try: check_is_hex_char(v) except ValueError as e: - raise ValueError(f"YourLastHex failed HexChar format validation: {e}") + raise ValueError( + f"YourLastHex failed HexChar format validation: {e}" + ) from e return v @field_validator("LastReceivedTimeUnixMs") @@ -181,7 +184,7 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 your_last_hex: str, last_received_time_unix_ms: int, send_time_unix_ms: int, - starting_over: bool, + starting_over: bool, # noqa: FBT001 ) -> None: self.tuple = HeartbeatB( FromGNodeAlias=from_g_node_alias, @@ -214,7 +217,7 @@ def type_to_tuple(cls, t: bytes) -> HeartbeatB: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: + def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: # noqa: C901 """ Deserialize a dictionary representation of a heartbeat.b.001 message object into a HeartbeatB python object for internal use. @@ -276,7 +279,7 @@ def check_is_hex_char(v: str) -> None: ValueError: if v is not HexChar format """ if not isinstance(v, str): - raise ValueError(f"<{v}> must be a hex char, but not even a string") + raise ValueError(f"<{v}> must be a hex char, but not even a string") # noqa: TRY004 if len(v) > 1: raise ValueError(f"<{v}> must be a hex char, but not of len 1") if v not in "0123456789abcdefABCDEF": @@ -295,12 +298,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -325,8 +326,6 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index 0c19ace8..50371971 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -73,7 +73,9 @@ class FibaroTempSensorSettings(FibaroTempSensorSettingsGt): @field_validator("rest") @classmethod - def _collapse_rest_url(cls, v: Optional[RESTPollerSettings]): + def _collapse_rest_url( + cls, v: Optional[RESTPollerSettings] + ) -> Optional[RESTPollerSettings]: if v is not None: # Collapse session.base_url and request.url into # request.url. @@ -121,7 +123,7 @@ def clear_property_cache(self) -> None: ]: self.__dict__.pop(prop, None) - def resolve_rest( + def resolve_rest( # noqa: C901, PLR0912 self, hubitat: HubitatRESTResolutionSettings, ) -> None: diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 160eec82..4989e5a4 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -120,11 +120,9 @@ def as_dict(self) -> Dict[str, Any]: d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) del d["TempUnit"] d["TempUnitGtEnumSymbol"] = Unit.value_to_symbol(self.TempUnit) - del d["TelemetryNameList"] - telemetry_name_list = [] - for elt in self.TelemetryNameList: - telemetry_name_list.append(TelemetryName.value_to_symbol(elt.value)) - d["TelemetryNameList"] = telemetry_name_list + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(elt) for elt in self.TelemetryNameList + ] return d def as_type(self) -> bytes: @@ -204,7 +202,7 @@ def type_to_tuple(cls, t: bytes) -> MultipurposeSensorCacGt: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: + def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: # noqa: C901 """ Deserialize a dictionary representation of a multipurpose.sensor.cac.gt.000 message object into a MultipurposeSensorCacGt python object for internal use. @@ -296,11 +294,11 @@ def dc_to_tuple(cls, dc: MultipurposeSensorCac) -> MultipurposeSensorCacGt: @classmethod def type_to_dc(cls, t: str) -> MultipurposeSensorCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) @classmethod def dc_to_type(cls, dc: MultipurposeSensorCac) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> MultipurposeSensorCac: diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 8f255988..4f6c2a67 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -119,10 +119,7 @@ def as_dict(self) -> Dict[str, Any]: if value is not None } # Recursively calling as_dict() - config_list = [] - for elt in self.ConfigList: - config_list.append(elt.as_dict()) - d["ConfigList"] = config_list + d["ConfigList"] = [elt.as_dict() for elt in self.ConfigList] return d def as_type(self) -> bytes: @@ -196,7 +193,7 @@ def type_to_tuple(cls, t: bytes) -> MultipurposeSensorComponentGt: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: + def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: # noqa: C901 """ Deserialize a dictionary representation of a multipurpose.sensor.component.gt.000 message object into a MultipurposeSensorComponentGt python object for internal use. @@ -282,11 +279,11 @@ def dc_to_tuple( @classmethod def type_to_dc(cls, t: str) -> MultipurposeSensorComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) @classmethod def dc_to_type(cls, dc: MultipurposeSensorComponent) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> MultipurposeSensorComponent: @@ -305,12 +302,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index a495d551..bbbbdb74 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -62,7 +62,7 @@ class RelayComponentGt(BaseModel): NormallyOpen: bool = Field( title="Normally Open", description=( - "Normally open relays default in the open position, meaning that when they're not " + "Normally open relays default in the open position, meaning that when they're not " "in use, there is no contact between the circuits. When power is introduced, an electromagnet " "pulls the first circuit into contact with the second, thereby closing the circuit " "and allowing power to flow through" @@ -156,7 +156,7 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 display_name: Optional[str], gpio: Optional[int], hw_uid: Optional[str], - normally_open: bool, + normally_open: bool, # noqa: FBT001 ) -> None: self.tuple = RelayComponentGt( ComponentId=component_id, @@ -257,11 +257,11 @@ def dc_to_tuple(cls, dc: RelayComponent) -> RelayComponentGt: @classmethod def type_to_dc(cls, t: str) -> RelayComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) or t @classmethod def dc_to_type(cls, dc: RelayComponent) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> RelayComponent: diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index 7e6de8ff..67010d7a 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -55,7 +55,7 @@ def __init__(self, component: RESTPollerComponent) -> None: @classmethod def tuple_to_type(cls, tpl: RESTPollerComponentGt) -> str: - return tpl.as_type() + return tpl.as_type().decode() @classmethod def type_to_tuple(cls, t: str) -> RESTPollerComponentGt: @@ -79,7 +79,7 @@ def type_to_dc(cls, t: str) -> RESTPollerComponent: @classmethod def dc_to_type(cls, dc: RESTPollerComponent) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode() @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerComponent: diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index 0ceee699..7c9c9512 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -179,11 +179,13 @@ class RESTPollerSettings(BaseModel): def url_args(self) -> dict: session_args = URLConfig.make_url_args(self.session.base_url) request_args = URLConfig.make_url_args(self.request.url) - if session_args is None and request_args is None: - if session_args is None and request_args is None: - raise ValueError( - "Neither session.base_url nor request.url produces a URL" - ) + if ( + session_args is None + and request_args is None + and session_args is None + and request_args is None + ): + raise ValueError("Neither session.base_url nor request.url produces a URL") if session_args is not None: url_args = session_args if request_args is not None: diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index ebb73ed6..f99372ec 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -246,11 +246,11 @@ def dc_to_tuple(cls, dc: SimpleTempSensorComponent) -> SimpleTempSensorComponent @classmethod def type_to_dc(cls, t: str) -> SimpleTempSensorComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) @classmethod def dc_to_type(cls, dc: SimpleTempSensorComponent) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> SimpleTempSensorComponent: diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 0a448165..910390e6 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -207,12 +207,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 952e2da3..8881ad14 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -321,11 +321,11 @@ def dc_to_tuple(cls, dc: ShNode) -> SpaceheatNodeGt: @classmethod def type_to_dc(cls, t: str) -> ShNode: - return cls.tuple_to_dc(cls.type_to_tuple(t)) + return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) @classmethod def dc_to_type(cls, dc: ShNode) -> str: - return cls.dc_to_tuple(dc).as_type() + return cls.dc_to_tuple(dc).as_type().decode("utf-8") @classmethod def dict_to_dc(cls, d: dict[Any, str]) -> ShNode: @@ -344,12 +344,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index fbad773c..064d6977 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -4,6 +4,7 @@ import logging from typing import Any, Dict, List, Literal +import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -122,10 +123,7 @@ def as_dict(self) -> Dict[str, Any]: if value is not None } # Recursively calling as_dict() - channels = [] - for elt in self.Channels: - channels.append(elt.as_dict()) - d["Channels"] = channels + d["Channels"] = [elt.as_dict() for elt in self.Channels] return d def as_type(self) -> bytes: @@ -199,7 +197,7 @@ def type_to_tuple(cls, t: bytes) -> TaDataChannels: return cls.dict_to_tuple(d) @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> TaDataChannels: + def dict_to_tuple(cls, d: dict[str, Any]) -> TaDataChannels: # noqa: C901 """ Deserialize a dictionary representation of a ta.data.channels.000 message object into a TaDataChannels python object for internal use. @@ -270,12 +268,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): @@ -300,8 +296,6 @@ def check_is_reasonable_unix_time_s(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeS format """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > v: # type: ignore[attr-defined] raise ValueError(f"<{v}> must be after Jan 1 2000") if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < v: # type: ignore[attr-defined] diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 384f257f..8b390f69 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -158,7 +158,7 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 self, telemetry_name: EnumTelemetryName, about_node_name: str, - report_on_change: bool, + report_on_change: bool, # noqa: FBT001 sample_period_s: int, exponent: int, unit: EnumUnit, @@ -261,12 +261,10 @@ def check_is_left_right_dot(v: str) -> None: Raises: ValueError: if v is not LeftRightDot format """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") + x: list[str] = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e first_word = x[0] first_char = first_word[0] if not first_char.isalpha(): diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index e71cd673..5dff1538 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -114,11 +114,10 @@ def as_dict(self) -> Dict[str, Any]: ).items() if value is not None } - del d["TelemetryNameList"] - telemetry_name_list = [] - for elt in self.TelemetryNameList: - telemetry_name_list.append(TelemetryName.value_to_symbol(elt.value)) - d["TelemetryNameList"] = telemetry_name_list + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(str(elt.value)) + for elt in self.TelemetryNameList + ] return d def as_type(self) -> bytes: From 409c5a45d5e9ea6c31c83f9a1192cf839f2b04f1 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 21 Aug 2024 10:27:22 -0400 Subject: [PATCH 081/168] Run 'ruff check' in pre-commit --- .pre-commit-config.yaml | 1 - src/gwproto/enums/actor_class.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9cba0376..0e16e120 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,5 +27,4 @@ repos: rev: v0.6.1 hooks: - id: ruff - args: ["--select", "I"] - id: ruff-format diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 0eac088d..a2caf4e6 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -83,10 +83,11 @@ def default(cls) -> "ActorClass": @classmethod def values(cls) -> List[str]: + # ruff: noqa: RUF100 """ Returns enum choices """ - return [elt.value for elt in cls] # noqa: ALL + return [elt.value for elt in cls] # noqa @classmethod def version(cls, value: str) -> str: From e28efbe51d7cea1cae6ef75be2d93c9e09802c94 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 21 Aug 2024 10:29:09 -0400 Subject: [PATCH 082/168] Correct use of 'ruff: noqa RUF100' directive --- src/gwproto/enums/actor_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index a2caf4e6..0dff6dd9 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -1,3 +1,4 @@ +# ruff: noqa: RUF100 from enum import auto from typing import List @@ -83,7 +84,6 @@ def default(cls) -> "ActorClass": @classmethod def values(cls) -> List[str]: - # ruff: noqa: RUF100 """ Returns enum choices """ From 9c2468448c8c6d3158ba3aa35dde4081c7f3fd76 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 15:22:35 -0400 Subject: [PATCH 083/168] Cleanups: update ruff warning control; update ruff version; remove bump-pydantic --- .pre-commit-config.yaml | 2 +- poetry.lock | 573 ++++++++---------- pyproject.toml | 28 +- tests/test_topic.py | 2 +- tests/types/test_electric_meter_cac_gt.py | 2 +- .../types/test_electric_meter_component_gt.py | 2 +- tests/types/test_gt_dispatch_boolean.py | 2 +- tests/types/test_gt_dispatch_boolean_local.py | 2 +- tests/types/test_gt_sh_status.py | 2 +- tests/types/test_heartbeat_b.py | 2 +- .../types/test_multipurpose_sensor_cac_gt.py | 2 +- .../test_multipurpose_sensor_component_gt.py | 2 +- .../test_pipe_flow_sensor_component_gt.py | 2 +- tests/types/test_relay_component_gt.py | 2 +- tests/types/test_resistive_heater_cac_gt.py | 2 +- .../test_resistive_heater_component_gt.py | 2 +- tests/types/test_simple_temp_sensor_cac_gt.py | 2 +- tests/types/test_spaceheat_node_gt.py | 2 +- tests/types/test_ta_data_channels.py | 2 +- .../types/test_telemetry_reporting_config.py | 2 +- 20 files changed, 286 insertions(+), 351 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e16e120..2297e80e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: args: [--py37-plus] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.1 + rev: v0.6.3 hooks: - id: ruff - id: ruff-format diff --git a/poetry.lock b/poetry.lock index 748e558b..450ddaff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -77,32 +77,15 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "bump-pydantic" -version = "0.8.0" -description = "Convert Pydantic from V1 to V2 ♻" -optional = false -python-versions = ">=3.8" -files = [ - {file = "bump_pydantic-0.8.0-py3-none-any.whl", hash = "sha256:6cbb4deb5869a69baa5a477f28f3e2d8fb09b687e114c018bd54470590ae7bf7"}, - {file = "bump_pydantic-0.8.0.tar.gz", hash = "sha256:6092e61930e85619e74eeb04131b4387feda16f02d8bb2e3cf9507fa492c69e9"}, -] - -[package.dependencies] -libcst = ">=0.4.2" -rich = "*" -typer = ">=0.7.0" -typing-extensions = "*" - [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -422,13 +405,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -470,46 +453,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "libcst" -version = "1.4.0" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." -optional = false -python-versions = ">=3.9" -files = [ - {file = "libcst-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:279b54568ea1f25add50ea4ba3d76d4f5835500c82f24d54daae4c5095b986aa"}, - {file = "libcst-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3401dae41fe24565387a65baee3887e31a44e3e58066b0250bc3f3ccf85b1b5a"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1989fa12d3cd79118ebd29ebe2a6976d23d509b1a4226bc3d66fcb7cb50bd5d"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addc6d585141a7677591868886f6bda0577529401a59d210aa8112114340e129"}, - {file = "libcst-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d71001cb25e94cfe8c3d997095741a8c4aa7a6d234c0f972bc42818c88dfaf"}, - {file = "libcst-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2d47de16d105e7dd5f4e01a428d9f4dc1e71efd74f79766daf54528ce37f23c3"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6227562fc5c9c1efd15dfe90b0971ae254461b8b6b23c1b617139b6003de1c1"}, - {file = "libcst-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3399e6c95df89921511b44d8c5bf6a75bcbc2d51f1f6429763609ba005c10f6b"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48601e3e590e2d6a7ab8c019cf3937c70511a78d778ab3333764531253acdb33"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f42797309bb725f0f000510d5463175ccd7155395f09b5e7723971b0007a976d"}, - {file = "libcst-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb4e42ea107a37bff7f9fdbee9532d39f9ea77b89caa5c5112b37057b12e0838"}, - {file = "libcst-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:9d0cc3c5a2a51fa7e1d579a828c0a2e46b2170024fd8b1a0691c8a52f3abb2d9"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7ece51d935bc9bf60b528473d2e5cc67cbb88e2f8146297e40ee2c7d80be6f13"}, - {file = "libcst-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:81653dea1cdfa4c6520a7c5ffb95fa4d220cbd242e446c7a06d42d8636bfcbba"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6abce0e66bba2babfadc20530fd3688f672d565674336595b4623cd800b91ef"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da9d7dc83801aba3b8d911f82dc1a375db0d508318bad79d9fb245374afe068"}, - {file = "libcst-1.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c54aa66c86d8ece9c93156a2cf5ca512b0dce40142fe9e072c86af2bf892411"}, - {file = "libcst-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:62e2682ee1567b6a89c91853865372bf34f178bfd237853d84df2b87b446e654"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8ecdba8934632b4dadacb666cd3816627a6ead831b806336972ccc4ba7ca0e9"}, - {file = "libcst-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e54c777b8d27339b70f304d16fc8bc8674ef1bd34ed05ea874bf4921eb5a313"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:061d6855ef30efe38b8a292b7e5d57c8e820e71fc9ec9846678b60a934b53bbb"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb0abf627ee14903d05d0ad9b2c6865f1b21eb4081e2c7bea1033f85db2b8bae"}, - {file = "libcst-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d024f44059a853b4b852cfc04fec33e346659d851371e46fc8e7c19de24d3da9"}, - {file = "libcst-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c6a8faab9da48c5b371557d0999b4ca51f4f2cbd37ee8c2c4df0ac01c781465"}, - {file = "libcst-1.4.0.tar.gz", hash = "sha256:449e0b16604f054fa7f27c3ffe86ea7ef6c409836fe68fe4e752a1894175db00"}, -] - -[package.dependencies] -pyyaml = ">=5.2" - -[package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.4)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.6)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.6.0)", "usort (==1.0.8.post1)"] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -745,38 +688,38 @@ files = [ [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] @@ -1273,13 +1216,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -1368,40 +1311,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.1" +version = "0.6.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9"}, - {file = "ruff-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae"}, - {file = "ruff-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb"}, - {file = "ruff-0.6.1-py3-none-win32.whl", hash = "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4"}, - {file = "ruff-0.6.1-py3-none-win_amd64.whl", hash = "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b"}, - {file = "ruff-0.6.1-py3-none-win_arm64.whl", hash = "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014"}, - {file = "ruff-0.6.1.tar.gz", hash = "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436"}, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, + {file = "ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3"}, + {file = "ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc"}, + {file = "ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8"}, + {file = "ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521"}, + {file = "ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb"}, + {file = "ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82"}, + {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, ] [[package]] @@ -1633,13 +1565,13 @@ test = ["pytest"] [[package]] name = "starlette" -version = "0.37.2" +version = "0.38.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, + {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"}, + {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"}, ] [package.dependencies] @@ -1677,23 +1609,6 @@ typing-extensions = ">=4.10.0" doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)"] test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] -[[package]] -name = "typer" -version = "0.12.3" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - [[package]] name = "types-pytz" version = "2024.1.0.20240417" @@ -1773,98 +1688,94 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchfiles" -version = "0.23.0" +version = "0.24.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ - {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"}, - {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"}, - {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"}, - {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"}, - {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"}, - {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"}, - {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"}, - {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"}, - {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"}, - {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"}, - {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"}, - {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"}, - {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"}, - {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"}, - {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"}, - {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"}, - {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"}, - {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"}, - {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"}, - {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"}, - {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"}, - {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"}, - {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"}, - {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"}, - {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"}, - {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"}, - {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"}, - {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"}, - {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"}, - {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"}, - {file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"}, - {file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"}, - {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"}, - {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"}, - {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"}, - {file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"}, - {file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"}, - {file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"}, - {file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"}, - {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"}, - {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"}, - {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"}, - {file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"}, - {file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"}, - {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"}, - {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"}, - {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"}, - {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"}, - {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"}, - {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"}, - {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"}, - {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"}, - {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"}, - {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"}, - {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"}, - {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"}, - {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"}, + {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, + {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"}, + {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"}, + {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"}, + {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"}, + {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"}, + {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"}, + {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"}, + {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"}, + {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"}, + {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"}, + {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"}, + {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"}, + {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"}, + {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"}, + {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"}, + {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"}, ] [package.dependencies] @@ -1872,111 +1783,129 @@ anyio = ">=3.0.0" [[package]] name = "websockets" -version = "12.0" +version = "13.0.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, + {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"}, + {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"}, + {file = "websockets-13.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f"}, + {file = "websockets-13.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543"}, + {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d"}, + {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f"}, + {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8"}, + {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b"}, + {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448"}, + {file = "websockets-13.0.1-cp310-cp310-win32.whl", hash = "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3"}, + {file = "websockets-13.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0"}, + {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7"}, + {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4"}, + {file = "websockets-13.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2"}, + {file = "websockets-13.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0"}, + {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e"}, + {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462"}, + {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501"}, + {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418"}, + {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df"}, + {file = "websockets-13.0.1-cp311-cp311-win32.whl", hash = "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f"}, + {file = "websockets-13.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075"}, + {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a"}, + {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956"}, + {file = "websockets-13.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af"}, + {file = "websockets-13.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf"}, + {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c"}, + {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4"}, + {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab"}, + {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d"}, + {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237"}, + {file = "websockets-13.0.1-cp312-cp312-win32.whl", hash = "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185"}, + {file = "websockets-13.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99"}, + {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa"}, + {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231"}, + {file = "websockets-13.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9"}, + {file = "websockets-13.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75"}, + {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553"}, + {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920"}, + {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329"}, + {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7"}, + {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2"}, + {file = "websockets-13.0.1-cp313-cp313-win32.whl", hash = "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb"}, + {file = "websockets-13.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b"}, + {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e"}, + {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2"}, + {file = "websockets-13.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b"}, + {file = "websockets-13.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae"}, + {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f"}, + {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603"}, + {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36"}, + {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a"}, + {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb"}, + {file = "websockets-13.0.1-cp38-cp38-win32.whl", hash = "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4"}, + {file = "websockets-13.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9"}, + {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc"}, + {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63"}, + {file = "websockets-13.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37"}, + {file = "websockets-13.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9"}, + {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97"}, + {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f"}, + {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4"}, + {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0"}, + {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d"}, + {file = "websockets-13.0.1-cp39-cp39-win32.whl", hash = "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58"}, + {file = "websockets-13.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333"}, + {file = "websockets-13.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026"}, + {file = "websockets-13.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5"}, + {file = "websockets-13.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980"}, + {file = "websockets-13.0.1-py3-none-any.whl", hash = "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817"}, + {file = "websockets-13.0.1.tar.gz", hash = "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e"}, ] [[package]] name = "xdoctest" -version = "1.1.6" +version = "1.2.0" description = "A rewrite of the builtin doctest module" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "xdoctest-1.1.6-py3-none-any.whl", hash = "sha256:a6f673df8c82b8fe0adc536f14c523464f25c6d2b733ed78888b8f8d6c46012e"}, - {file = "xdoctest-1.1.6.tar.gz", hash = "sha256:00ec7bde36addbedf5d1db0db57b6b669a7a4b29ad2d16480950556644f02109"}, + {file = "xdoctest-1.2.0-py3-none-any.whl", hash = "sha256:0f1ecf5939a687bd1fc8deefbff1743c65419cce26dff908f8b84c93fbe486bc"}, + {file = "xdoctest-1.2.0.tar.gz", hash = "sha256:d8cfca6d8991e488d33f756e600d35b9fdf5efd5c3a249d644efcbbbd2ed5863"}, ] [package.dependencies] -colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} -Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} +colorama = {version = ">=0.4.1", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} +Pygments = {version = ">=2.4.1", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} [package.extras] -all = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)", "typing (>=3.7.4)"] -all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)", "typing (==3.7.4)"] -colors = ["Pygments", "Pygments", "colorama"] -jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "nbconvert"] -optional = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"] -optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] -tests = ["pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "typing (>=3.7.4)"] -tests-binary = ["cmake", "cmake", "ninja", "ninja", "pybind11", "pybind11", "scikit-build", "scikit-build"] +all = ["IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)"] +all-strict = ["IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)"] +colors = ["Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "colorama (>=0.4.1)"] +colors-strict = ["Pygments (==2.0.0)", "Pygments (==2.4.1)", "colorama (==0.4.1)"] +docs = ["Pygments (>=2.9.0)", "myst-parser (>=0.18.0)", "sphinx (>=5.0.1)", "sphinx-autoapi (>=1.8.4)", "sphinx-autobuild (>=2021.3.14)", "sphinx-reredirects (>=0.0.1)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-napoleon (>=0.7)"] +docs-strict = ["Pygments (==2.9.0)", "myst-parser (==0.18.0)", "sphinx (==5.0.1)", "sphinx-autoapi (==1.8.4)", "sphinx-autobuild (==2021.3.14)", "sphinx-reredirects (==0.0.1)", "sphinx-rtd-theme (==1.0.0)", "sphinxcontrib-napoleon (==0.7)"] +jupyter = ["IPython (>=7.23.1)", "attrs (>=19.2.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)"] +jupyter-strict = ["IPython (==7.23.1)", "attrs (==19.2.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)"] +optional = ["IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"] +optional-strict = ["IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] +tests = ["pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)"] +tests-binary = ["cmake (>=3.21.2)", "cmake (>=3.25.0)", "ninja (>=1.10.2)", "ninja (>=1.11.1)", "pybind11 (>=2.10.3)", "pybind11 (>=2.7.1)", "scikit-build (>=0.11.1)", "scikit-build (>=0.16.1)"] tests-binary-strict = ["cmake (==3.21.2)", "cmake (==3.25.0)", "ninja (==1.10.2)", "ninja (==1.11.1)", "pybind11 (==2.10.3)", "pybind11 (==2.7.1)", "scikit-build (==0.11.1)", "scikit-build (==0.16.1)"] -tests-strict = ["pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "typing (==3.7.4)"] +tests-strict = ["pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)"] [[package]] name = "yarl" @@ -2084,4 +2013,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "ad46598280e2d1bda9c28e899ce1854c6746033f250f5fc5a3528ba10001318a" +content-hash = "c576eedd3967bc53ab5985f32d643226d55af545475db0ea04f5be0bea275b02" diff --git a/pyproject.toml b/pyproject.toml index 92b32e71..58bfc54e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gridworks-protocol" -version = "0.7.5" +version = "0.8.0" description = "Gridworks Protocol" authors = ["Jessica Millar "] license = "MIT" @@ -24,7 +24,6 @@ pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" pendulum = "2.1.2" -ruff = "^0.6.1" [tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" @@ -44,7 +43,7 @@ xdoctest = {extras = ["colors"], version = ">=0.15.10"} myst-parser = {version = ">=0.16.1"} types-pytz = ">=2022.4.0.0" rich = ">=12.6.0" -bump-pydantic = "^0.8.0" +ruff = "^0.6.3" [tool.coverage.paths] source = ["src", "*/site-packages"] @@ -136,21 +135,28 @@ ignore = [ "G004", # Suppress for now; lots of them generated by code generation. "ISC001", "N801", - "PGH004", # Suppress for now; lots of them generated by code generation. + "N803", # Protocol sometimes calls for CamelCase arguments. + "N818", # We disagree with Pep8 about naming exceptions. + "PGH004", # ruff and IDE can disagree, in which case a simple # noqa might be need. "PLR0904", "PLR2004", # Suppress for now; lots of them generated by code generation. "PLW1514", - "TRY003", # Suppress for now; lots of them generated by code generation. + "TRY003", # Many of our ValueErrors contain messages that are not plausibly resuable. + "RUF100", # ruff and IDE often disagree about whether a 'noqa' is in use. "W191", ] + + [tool.ruff.lint.extend-per-file-ignores] "tests/**/*.py" = [ - # at least this three should be fine in tests: + "ARG001", # Unused function args -> fixtures nevertheless are functionally relevant... + "C901", # Complexity + "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() + "G004", # Ok to use f-strings in logging in test code + "PLR0912", # Complexity + "PLR0913", # Complexity + "PLR0915", # Complexity + "PT011", # It's not practical to match strings in exceptions caught by pytest.raises() "S101", # asserts allowed in tests... -# "ARG", # Unused function args -> fixtures nevertheless are functionally relevant... -# "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() -# # The below are debateable -# "PLR2004", # Magic value used in comparison, ... -# "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes ] diff --git a/tests/test_topic.py b/tests/test_topic.py index f6937cf3..47b462aa 100644 --- a/tests/test_topic.py +++ b/tests/test_topic.py @@ -11,7 +11,7 @@ def test_mqtt_topic_encode() -> None: def test_mqtt_topic_decode() -> None: - with pytest.raises(ValueError): # noqa: PT011 + with pytest.raises(ValueError): MQTTTopic.decode("") decoded = MQTTTopic.decode("foo/bar/baz") diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 6589d9dd..b267619b 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import ElectricMeterCacGt_Maker as Maker -def test_electric_meter_cac_gt_generated() -> None: # noqa: PLR0915 +def test_electric_meter_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", "MakeModelGtEnumSymbol": "d300635e", diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index f0b6d27e..397c0cf2 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -11,7 +11,7 @@ ) -def test_electric_meter_component_gt_generated() -> None: # noqa: PLR0915 +def test_electric_meter_component_gt_generated() -> None: d = { "ComponentId": "2dfb0cb6-6015-4273-b02b-bd446cc785d7", "ComponentAttributeClassId": "204832ef-0c88-408b-9640-264d2ee74914", diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py index 5b034ef4..cd659806 100644 --- a/tests/types/test_gt_dispatch_boolean.py +++ b/tests/types/test_gt_dispatch_boolean.py @@ -9,7 +9,7 @@ from gwproto.types import GtDispatchBoolean_Maker as Maker -def test_gt_dispatch_boolean_generated() -> None: # noqa: PLR0915 +def test_gt_dispatch_boolean_generated() -> None: d = { "AboutNodeName": "a.elt1.relay", "ToGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py index 9fbc266a..2371adce 100644 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ b/tests/types/test_gt_dispatch_boolean_local.py @@ -9,7 +9,7 @@ from gwproto.types import GtDispatchBooleanLocal_Maker as Maker -def test_gt_dispatch_boolean_local_generated() -> None: # noqa: PLR0915 +def test_gt_dispatch_boolean_local_generated() -> None: d = { "RelayState": 1, "AboutNodeName": "a.elt1.relay", diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index 0abd8157..c4ba62f4 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -9,7 +9,7 @@ from gwproto.types import GtShStatus_Maker as Maker -def test_gt_sh_status_generated() -> None: # noqa: PLR0915 +def test_gt_sh_status_generated() -> None: d = { "FromGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", "FromGNodeId": "0384ef21-648b-4455-b917-58a1172d7fc1", diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index 19492a13..1d75a83c 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -9,7 +9,7 @@ from gwproto.types import HeartbeatB_Maker as Maker -def test_heartbeat_b_generated() -> None: # noqa: PLR0915 +def test_heartbeat_b_generated() -> None: d = { "FromGNodeAlias": "d1.isone.ver.keene.holly", "FromGNodeInstanceId": "97eba574-bd20-45b5-bf82-9ba2f492d8f6", diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 3337031b..59c1a65f 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import MultipurposeSensorCacGt_Maker as Maker -def test_multipurpose_sensor_cac_gt_generated() -> None: # noqa: PLR0915 +def test_multipurpose_sensor_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", "MakeModelGtEnumSymbol": "09185ae3", diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index 3579b4ae..92308e0f 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -11,7 +11,7 @@ ) -def test_multipurpose_sensor_component_gt_generated() -> None: # noqa: PLR0915 +def test_multipurpose_sensor_component_gt_generated() -> None: d = { "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index d573bc57..258ef71c 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import PipeFlowSensorComponentGt_Maker as Maker -def test_pipe_flow_sensor_component_gt_generated() -> None: # noqa: PLR0915 +def test_pipe_flow_sensor_component_gt_generated() -> None: d = { "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", "ComponentAttributeClassId": "13d916dc-8764-4b16-b85d-b8ead3e2fc80", diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index a4753e1e..0e469f3e 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import RelayComponentGt_Maker as Maker -def test_relay_component_gt_generated() -> None: # noqa: PLR0915 +def test_relay_component_gt_generated() -> None: d = { "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 9b2b05bd..142fb1b7 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import ResistiveHeaterCacGt_Maker as Maker -def test_resistive_heater_cac_gt_generated() -> None: # noqa: PLR0915 +def test_resistive_heater_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", "MakeModelGtEnumSymbol": "00000000", diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index 1da807dc..fc968072 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -9,7 +9,7 @@ from gwproto.types import ResistiveHeaterComponentGt_Maker as Maker -def test_resistive_heater_component_gt_generated() -> None: # noqa: PLR0915 +def test_resistive_heater_component_gt_generated() -> None: d = { "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index 0866fd5b..b55976fb 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -10,7 +10,7 @@ from gwproto.types import SimpleTempSensorCacGt_Maker as Maker -def test_simple_temp_sensor_cac_gt_generated() -> None: # noqa: PLR0915 +def test_simple_temp_sensor_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", "MakeModelGtEnumSymbol": "acd93fb3", diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 4e01a4a3..7e81d669 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -10,7 +10,7 @@ from gwproto.types import SpaceheatNodeGt_Maker as Maker -def test_spaceheat_node_gt_generated() -> None: # noqa: PLR0915 +def test_spaceheat_node_gt_generated() -> None: d = { "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", "Alias": "a.elt1", diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py index b52128d6..d373c077 100644 --- a/tests/types/test_ta_data_channels.py +++ b/tests/types/test_ta_data_channels.py @@ -9,7 +9,7 @@ from gwproto.types import TaDataChannels_Maker as Maker -def test_ta_data_channels_generated() -> None: # noqa: PLR0915 +def test_ta_data_channels_generated() -> None: d = { "TerminalAssetGNodeAlias": "hw1.isone.me.versant.keene.oak.ta", "TerminalAssetGNodeId": "7e152072-c91b-49d2-9ebd-f4fe1b684d06", diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index e247cb8b..bcf9384c 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -10,7 +10,7 @@ from gwproto.types import TelemetryReportingConfig_Maker as Maker -def test_telemetry_reporting_config_generated() -> None: # noqa: PLR0915 +def test_telemetry_reporting_config_generated() -> None: d = { "TelemetryNameGtEnumSymbol": "af39eec9", "AboutNodeName": "a.elt1", From 9b1c7e16e18f7cd2aa1453170c2e4ea650ac6713 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 15:42:08 -0400 Subject: [PATCH 084/168] Remove pendulum --- poetry.lock | 72 +------------------ pyproject.toml | 1 - src/gwproto/property_format.py | 20 +++--- src/gwproto/types/gt_dispatch_boolean.py | 6 +- .../types/gt_dispatch_boolean_local.py | 6 +- .../types/gt_driver_booleanactuator_cmd.py | 6 +- .../types/gt_sh_booleanactuator_cmd_status.py | 6 +- .../gt_sh_multipurpose_telemetry_status.py | 6 +- .../types/gt_sh_simple_telemetry_status.py | 6 +- src/gwproto/types/gt_sh_status.py | 6 +- ...t_sh_telemetry_from_multipurpose_sensor.py | 6 +- src/gwproto/types/gt_telemetry.py | 7 +- src/gwproto/types/heartbeat_b.py | 6 +- src/gwproto/types/ta_data_channels.py | 6 +- .../types/telemetry_snapshot_spaceheat.py | 6 +- 15 files changed, 48 insertions(+), 118 deletions(-) diff --git a/poetry.lock b/poetry.lock index 450ddaff..1ccfa8d6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -791,40 +791,6 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[[package]] -name = "pendulum" -version = "2.1.2" -description = "Python datetimes made easy" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pendulum-2.1.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe"}, - {file = "pendulum-2.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739"}, - {file = "pendulum-2.1.2-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394"}, - {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0"}, - {file = "pendulum-2.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3"}, - {file = "pendulum-2.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b"}, - {file = "pendulum-2.1.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360"}, - {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0"}, - {file = "pendulum-2.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087"}, - {file = "pendulum-2.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db"}, - {file = "pendulum-2.1.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002"}, - {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5"}, - {file = "pendulum-2.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b"}, - {file = "pendulum-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b"}, - {file = "pendulum-2.1.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116"}, - {file = "pendulum-2.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052"}, - {file = "pendulum-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be"}, - {file = "pendulum-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269"}, - {file = "pendulum-2.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a"}, - {file = "pendulum-2.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7"}, - {file = "pendulum-2.1.2.tar.gz", hash = "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207"}, -] - -[package.dependencies] -python-dateutil = ">=2.6,<3.0" -pytzdata = ">=2020.1" - [[package]] name = "pep8-naming" version = "0.14.1" @@ -1081,20 +1047,6 @@ 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 = "pytz" version = "2024.1" @@ -1106,17 +1058,6 @@ files = [ {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] -[[package]] -name = "pytzdata" -version = "2020.1" -description = "The Olson timezone database for Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pytzdata-2020.1-py2.py3-none-any.whl", hash = "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f"}, - {file = "pytzdata-2020.1.tar.gz", hash = "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540"}, -] - [[package]] name = "pyupgrade" version = "3.17.0" @@ -1336,17 +1277,6 @@ files = [ {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, ] -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - [[package]] name = "sniffio" version = "1.3.1" @@ -2013,4 +1943,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "c576eedd3967bc53ab5985f32d643226d55af545475db0ea04f5be0bea275b02" +content-hash = "e7c21e590eb34a76e99b81d61a6e7180cc49bbc1b3544a3d578764caa1c297cc" diff --git a/pyproject.toml b/pyproject.toml index 58bfc54e..db179e20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,6 @@ python = "^3.11" pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" -pendulum = "2.1.2" [tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 53d3a615..929fc34f 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -2,9 +2,9 @@ import string import struct +from datetime import datetime, timezone from typing import Any, Callable, List -import pendulum import pydantic @@ -233,28 +233,30 @@ def check_is_positive_integer(candidate: int) -> None: def is_reasonable_unix_time_ms(candidate: int) -> bool: - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > candidate: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > candidate: # type: ignore[attr-defined] return False - return pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 >= candidate + return ( + int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) >= candidate + ) def check_is_reasonable_unix_time_ms(candidate: int) -> None: - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > candidate: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > candidate: raise ValueError("ReasonableUnixTimeMs must be after 2000 AD") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < candidate: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < candidate: raise ValueError("ReasonableUnixTimeMs must be before 3000 AD") def is_reasonable_unix_time_s(candidate: int) -> bool: - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > candidate: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > candidate: return False - return pendulum.parse("3000-01-01T00:00:00Z").int_timestamp >= candidate # type: ignore[attr-defined] + return int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) >= candidate def check_is_reasonable_unix_time_s(candidate: int) -> None: - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > candidate: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > candidate: raise ValueError("ReasonableUnixTimeS must be after 2000 AD") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < candidate: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()): raise ValueError("ReasonableUnixTimeS must be before 3000 AD") diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 0c9c71c9..c4d9dbe0 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -317,9 +317,9 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 1446cee7..14152c91 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -282,7 +282,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 7bf8db7d..1994c7be 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -257,7 +257,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index a764d7ca..854ee212 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -238,7 +238,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index befe37d8..be831ba1 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal, Self -import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName @@ -286,7 +286,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 4d502d79..9c5f25c9 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal, Self -import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName as EnumTelemetryName @@ -269,7 +269,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 9e6c431e..1099f822 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -363,9 +363,9 @@ def check_is_reasonable_unix_time_s(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeS format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index a93c73e8..ef640b26 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal, Self -import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName @@ -277,7 +277,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index d451dedf..204bc0ad 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -2,6 +2,7 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, Literal from pydantic import BaseModel, Field, field_validator @@ -214,9 +215,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - import pendulum - - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index 5f87a375..ed82c238 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -326,9 +326,9 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 064d6977..55d3a8c5 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal -import pendulum from pydantic import BaseModel, Field, field_validator from gwproto.errors import SchemaError @@ -296,9 +296,9 @@ def check_is_reasonable_unix_time_s(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeS format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 5dff1538..3bd4e0fd 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -2,9 +2,9 @@ import json import logging +from datetime import datetime, timezone from typing import Any, Dict, List, Literal, Self -import pendulum from pydantic import BaseModel, Field, field_validator, model_validator from gwproto.enums import TelemetryName @@ -278,7 +278,7 @@ def check_is_reasonable_unix_time_ms(v: int) -> None: Raises: ValueError: if v is not ReasonableUnixTimeMs format """ - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] + if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] + if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: raise ValueError(f"<{v}> must be before Jan 1 3000") From 8f5d5bed956f7b2092b8d08e78b887b7acb19b9b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 15:44:29 -0400 Subject: [PATCH 085/168] CI: Add and favor python 3.12 --- .github/workflows/tests.yml | 13 ++++++------- noxfile.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62b275ff..75918234 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,15 +12,14 @@ jobs: fail-fast: false matrix: include: - - { python: "3.11", os: "ubuntu-latest", session: "pre-commit" } + - { python: "3.12", os: "ubuntu-latest", session: "pre-commit" } # - { python: "3.12", os: "ubuntu-latest", session: "mypy" } - { python: "3.11", os: "ubuntu-latest", session: "tests" } - # - { python: "3.12", os: "ubuntu-latest", session: "tests" } - # - { python: "3.12", os: "ubuntu-latest", session: "tests" } - - { python: "3.11", os: "windows-latest", session: "tests" } - - { python: "3.11", os: "macos-latest", session: "tests" } - - { python: "3.11", os: "ubuntu-latest", session: "xdoctest" } - - { python: "3.11", os: "ubuntu-latest", session: "docs-build" } + - { python: "3.12", os: "ubuntu-latest", session: "tests" } + - { python: "3.12", os: "windows-latest", session: "tests" } + - { python: "3.12", os: "macos-latest", session: "tests" } + - { python: "3.12", os: "ubuntu-latest", session: "xdoctest" } + - { python: "3.12", os: "ubuntu-latest", session: "docs-build" } env: NOXSESSION: ${{ matrix.session }} FORCE_COLOR: "1" diff --git a/noxfile.py b/noxfile.py index 602f2ef4..675327b0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -25,7 +25,7 @@ package = "gwproto" -python_versions = ["3.11"] +python_versions = ["3.12", "3.11"] nox.needs_version = ">= 2021.6.6" nox.options.sessions = ( From a9ef54ba71acfb43412a0563114d38d11e91dc41 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 15:52:27 -0400 Subject: [PATCH 086/168] CI: run coverage under 3.12 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 75918234..b8635378 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -123,7 +123,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Upgrade pip run: | From f38d33e0aa13bff4c9e37d359a7c34c84d0db85a Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 20:22:11 -0400 Subject: [PATCH 087/168] Use enum.Enum._missing_() to alleviate need for a validator; also move 'values()' function to our enum base class --- src/gwproto/enums/actor_class.py | 7 ------- src/gwproto/enums/better_str_enum.py | 19 +++++++++++++++++++ src/gwproto/enums/local_comm_interface.py | 7 ------- src/gwproto/enums/make_model.py | 7 ------- src/gwproto/enums/role.py | 7 ------- src/gwproto/enums/telemetry_name.py | 7 ------- src/gwproto/enums/unit.py | 7 ------- 7 files changed, 19 insertions(+), 42 deletions(-) diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 0dff6dd9..8d5906a5 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -82,13 +82,6 @@ def default(cls) -> "ActorClass": """ return cls.NoActor - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa - @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/better_str_enum.py b/src/gwproto/enums/better_str_enum.py index 364a3c98..24bed99c 100644 --- a/src/gwproto/enums/better_str_enum.py +++ b/src/gwproto/enums/better_str_enum.py @@ -1,5 +1,8 @@ +from enum import auto from enum import StrEnum from typing import Any +from typing import Optional +from typing import Self class BetterStrEnum(StrEnum): @@ -11,3 +14,19 @@ def _generate_next_value_( last_values: list[Any], # noqa: ARG004 ) -> str: return name + + @classmethod + def values(cls) -> list[str]: + return [str(elt) for elt in cls] + + @classmethod + def default(cls) -> Optional[Self]: + return None + + + @classmethod + def _missing_(cls, value: str) -> Self: + default = cls.default() + if default is None: + raise ValueError(f"'{value}' is not valid {cls.__name__}") + return default diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 950b9d99..782fb752 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -44,13 +44,6 @@ def default(cls) -> "LocalCommInterface": """ return cls.UNKNOWN - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa: ALL - @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index dbb186a1..4b58d8c3 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -89,13 +89,6 @@ def default(cls) -> "MakeModel": """ return cls.UNKNOWNMAKE__UNKNOWNMODEL - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa: ALL - @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 31f9f811..79b76adc 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -77,13 +77,6 @@ def default(cls) -> "Role": """ return cls.Unknown - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa: ALL - @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index 0c38dcff..a1bbba38 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -66,13 +66,6 @@ def default(cls) -> "TelemetryName": """ return cls.Unknown - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa: ALL - @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 7dc04a1e..3c806f9a 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -48,13 +48,6 @@ def default(cls) -> "Unit": """ return cls.Unknown - @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] # noqa: ALL - @classmethod def version(cls, value: str) -> str: """ From a16f72892f2deb5b7f876764ee0cc17f277aed40 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 30 Aug 2024 20:25:36 -0400 Subject: [PATCH 088/168] ruff format/check --- src/gwproto/enums/better_str_enum.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/gwproto/enums/better_str_enum.py b/src/gwproto/enums/better_str_enum.py index 24bed99c..64e1064e 100644 --- a/src/gwproto/enums/better_str_enum.py +++ b/src/gwproto/enums/better_str_enum.py @@ -1,8 +1,5 @@ -from enum import auto from enum import StrEnum -from typing import Any -from typing import Optional -from typing import Self +from typing import Any, Optional, Self class BetterStrEnum(StrEnum): @@ -23,7 +20,6 @@ def values(cls) -> list[str]: def default(cls) -> Optional[Self]: return None - @classmethod def _missing_(cls, value: str) -> Self: default = cls.default() From 4aebd522e34c5eb937a47ae8789cb45912f9da57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:30:11 +0000 Subject: [PATCH 089/168] build(deps): bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.0 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 353d2985..4b189b64 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,14 +57,14 @@ jobs: - name: Publish package on PyPI if: steps.check-version.outputs.tag - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} - name: Publish package on TestPyPI if: "! steps.check-version.outputs.tag" - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 with: user: __token__ password: ${{ secrets.TEST_PYPI_TOKEN }} From adbb30dfa5aaae382c9b32c68bd93f9c4fbdfaa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:33:29 +0000 Subject: [PATCH 090/168] build(deps): bump myst-parser from 3.0.1 to 4.0.0 Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 3.0.1 to 4.0.0. - [Release notes](https://github.com/executablebooks/MyST-Parser/releases) - [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/MyST-Parser/compare/v3.0.1...v4.0.0) --- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 1ccfa8d6..f44fd56c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" From 7c2333f8f6ed0fbf917883decf12689b6dd3d15b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:34:27 +0000 Subject: [PATCH 091/168] build(deps): bump myst-parser from 3.0.1 to 4.0.0 in /docs Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 3.0.1 to 4.0.0. - [Release notes](https://github.com/executablebooks/MyST-Parser/releases) - [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/MyST-Parser/compare/v3.0.1...v4.0.0) --- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1c7a572a..8ab621be 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ furo==2024.8.6 sphinx==8.0.2 sphinx-click==6.0.0 -myst_parser==3.0.1 +myst_parser==4.0.0 sphinx-rtd-theme From 5325e321a3c394d16c058167dd724a661c65a38b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 3 Sep 2024 14:16:52 -0400 Subject: [PATCH 092/168] Reduced cacs --- pyproject.toml | 4 + src/gwproto/__init__.py | 4 +- src/gwproto/data_classes/hardware_layout.py | 91 ++--- src/gwproto/decoders.py | 2 +- src/gwproto/default_decoders.py | 56 +-- src/gwproto/enums/actor_class.py | 4 +- src/gwproto/enums/local_comm_interface.py | 4 +- src/gwproto/enums/make_model.py | 4 +- src/gwproto/enums/role.py | 4 +- src/gwproto/enums/symbolized.py | 11 + src/gwproto/enums/telemetry_name.py | 4 +- src/gwproto/enums/unit.py | 4 +- src/gwproto/types/__init__.py | 25 +- src/gwproto/types/cacs.py | 27 ++ .../types/component_attribute_class_gt.py | 248 +------------ src/gwproto/types/electric_meter_cac_gt.py | 337 +---------------- .../types/fibaro_smart_implant_cac_gt.py | 67 +--- src/gwproto/types/hubitat_cac_gt.py | 23 -- src/gwproto/types/hubitat_poller_cac_gt.py | 29 -- src/gwproto/types/hubitat_tank_cac_gt.py | 65 ---- .../types/multipurpose_sensor_cac_gt.py | 343 +----------------- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 263 +------------- src/gwproto/types/relay_cac_gt.py | 263 +------------- src/gwproto/types/resistive_heater_cac_gt.py | 302 +-------------- src/gwproto/types/rest_poller_cac_gt.py | 67 +--- .../types/simple_temp_sensor_cac_gt.py | 320 +--------------- src/gwproto/types/web_server_cac_gt.py | 23 -- src/gwproto/utils.py | 18 + tests/cac_load_utils.py | 115 ++++++ tests/data_classes/test_electric_meter_cac.py | 52 +-- .../test_electric_meter_component.py | 5 +- tests/test_misc/test_flush_and_load_house.py | 14 +- .../test_component_attribute_class_gt.py | 88 +---- tests/types/test_electric_meter_cac_gt.py | 139 +------ .../types/test_multipurpose_sensor_cac_gt.py | 155 +------- tests/types/test_pipe_flow_sensor_cac_gt.py | 104 +----- tests/types/test_relay_cac_gt.py | 108 +----- tests/types/test_resistive_heater_cac_gt.py | 122 +------ tests/types/test_simple_temp_sensor_cac_gt.py | 148 +------- 39 files changed, 395 insertions(+), 3267 deletions(-) create mode 100644 src/gwproto/enums/symbolized.py create mode 100644 src/gwproto/types/cacs.py create mode 100644 tests/cac_load_utils.py diff --git a/pyproject.toml b/pyproject.toml index db179e20..b3988b07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,6 +127,8 @@ ignore = [ "CPY", "D", "DOC", + "E272", # Formatter + "E241", # Formatter "E302", "E501", "EM", @@ -143,6 +145,7 @@ ignore = [ "TRY003", # Many of our ValueErrors contain messages that are not plausibly resuable. "RUF100", # ruff and IDE often disagree about whether a 'noqa' is in use. "W191", + "W291", # Formatter ] @@ -151,6 +154,7 @@ ignore = [ "tests/**/*.py" = [ "ARG001", # Unused function args -> fixtures nevertheless are functionally relevant... "C901", # Complexity + "ERA001", # We need to be able comment out code in tests, at least during refactoring. "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() "G004", # Ok to use f-strings in logging in test code "PLR0912", # Complexity diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index b2e11b14..3aaa1fef 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -56,7 +56,7 @@ "default_cac_decoder", "default_component_decoder", "get_pydantic_literal_type_name", - "messages", - "property_format", + "messages", # noqa: F822 + "property_format", # noqa: F822 "pydantic_named_types", ] diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 81e40a38..8869d860 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -17,7 +17,6 @@ from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.component import Component -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) @@ -33,15 +32,10 @@ ) from gwproto.enums import ActorClass, Role, TelemetryName from gwproto.types import ( - ElectricMeterCacGt_Maker, - MultipurposeSensorCacGt_Maker, - PipeFlowSensorCacGt_Maker, + ComponentAttributeClassGt, PipeFlowSensorComponentGt_Maker, - RelayCacGt_Maker, RelayComponentGt_Maker, - ResistiveHeaterCacGt_Maker, ResistiveHeaterComponentGt_Maker, - SimpleTempSensorCacGt_Maker, SimpleTempSensorComponentGt_Maker, SpaceheatNodeGt_Maker, ) @@ -66,54 +60,42 @@ class LoadError: exception: Exception -def load_cacs( # noqa: C901 +def load_cacs( layout: dict[str, Any], - raise_errors: bool = True, # noqa: FBT001, FBT002 + *, + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, -) -> dict[str, Any]: +) -> dict[str, ComponentAttributeClassGt]: if errors is None: errors = [] + if cac_decoder is None: + cac_decoder = default_cac_decoder cacs = {} - for type_name, maker_class in [ - ("RelayCacs", RelayCacGt_Maker), - ("ResistiveHeaterCacs", ResistiveHeaterCacGt_Maker), - ("ElectricMeterCacs", ElectricMeterCacGt_Maker), - ("PipeFlowSensorCacs", PipeFlowSensorCacGt_Maker), - ("MultipurposeSensorCacs", MultipurposeSensorCacGt_Maker), - ("SimpleTempSensorCacs", SimpleTempSensorCacGt_Maker), + for type_name in [ + "RelayCacs", + "ResistiveHeaterCacs", + "ElectricMeterCacs", + "PipeFlowSensorCacs", + "MultipurposeSensorCacs", + "SimpleTempSensorCacs", + "OtherCacs", ]: - for d in layout.get(type_name, []): + for cac_dict in layout.get(type_name, ()): try: - cacs[d["ComponentAttributeClassId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] - d - ) + cac = cac_decoder.decode(cac_dict) + cacs[cac.ComponentAttributeClassId] = cac except Exception as e: # noqa: PERF203 if raise_errors: raise - errors.append(LoadError(type_name, d, e)) - if cac_decoder is None: - cac_decoder = default_cac_decoder - for d in layout.get("OtherCacs", []): - cac_type = d.get("TypeName", "") - try: - if cac_type and cac_type in cac_decoder: - cac = cac_decoder.decode_to_data_class(d) - else: - cac = ComponentAttributeClass( - component_attribute_class_id=d["ComponentAttributeClassId"] - ) - cacs[d["ComponentAttributeClassId"]] = cac - except Exception as e: - if raise_errors: - raise - errors.append(LoadError("OtherCacs", d, e)) + errors.append(LoadError(type_name, cac_dict, e)) return cacs def load_components( # noqa: C901 layout: dict[Any, Any], - raise_errors: bool = True, # noqa: FBT001, FBT002 + *, + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, component_decoder: Optional[ComponentDecoder] = None, ) -> dict[Any, Any]: @@ -156,7 +138,8 @@ def load_components( # noqa: C901 def load_nodes( layout: dict[Any, Any], - raise_errors: bool = True, # noqa: FBT001, FBT002 + *, + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, included_node_names: Optional[set[str]] = None, ) -> dict[Any, Any]: @@ -178,7 +161,8 @@ def load_nodes( def resolve_links( nodes: dict[str, ShNode], components: dict[str, Component], - raise_errors: bool = True, # noqa: FBT001, FBT002 + *, + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, ) -> None: if errors is None: @@ -206,7 +190,7 @@ def resolve_links( class HardwareLayout: layout: dict[Any, Any] - cacs: dict[str, ComponentAttributeClass] + cacs: dict[str, ComponentAttributeClassGt] components: dict[str, Component] components_by_type: dict[Type, list[Component]] nodes: dict[str, ShNode] @@ -215,19 +199,22 @@ class HardwareLayout: def __init__( self, layout: dict[Any, Any], - cacs: Optional[dict[str, ComponentAttributeClass]] = None, + *, + cacs: Optional[dict[str, ComponentAttributeClassGt]] = None, components: Optional[dict[str, Component]] = None, nodes: Optional[dict[str, ShNode]] = None, ) -> None: self.layout = copy.deepcopy(layout) if cacs is None: - cacs = ComponentAttributeClass.by_id - self.cacs = dict(cacs) + self.cacs = {} + else: + self.cacs = dict(cacs) if components is None: - components = Component.by_id - self.components = dict(components) + self.components = {} + else: + self.components = dict(components) self.components_by_type = defaultdict(list) - for component in components.values(): + for component in self.components.values(): self.components_by_type[type(component)].append(component) if nodes is None: nodes = ShNode.by_id @@ -248,6 +235,7 @@ def clear_property_cache(self) -> None: def load( # noqa: PLR0913, PLR0917, RUF100 cls, layout_path: Path | str, + *, included_node_names: Optional[set[str]] = None, raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, @@ -269,8 +257,9 @@ def load( # noqa: PLR0913, PLR0917, RUF100 def load_dict( # noqa: PLR0913, PLR0917, RUF100 cls, layout: dict[Any, Any], + *, included_node_names: Optional[set[str]] = None, - raise_errors: bool = True, # noqa: FBT001, FBT002 + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, component_decoder: Optional[ComponentDecoder] = None, @@ -311,7 +300,7 @@ def node(self, alias: str, default: Any = None) -> ShNode: # noqa: ANN401 def component(self, node_alias: str) -> Optional[Component]: return self.component_from_node(self.node(node_alias, None)) - def cac(self, node_alias: str) -> Optional[ComponentAttributeClass]: + def cac(self, node_alias: str) -> Optional[ComponentAttributeClassGt]: return self.cac_from_component(self.component(node_alias)) def get_component_as_type(self, component_id: str, type_: Type[T]) -> Optional[T]: @@ -347,7 +336,7 @@ def component_from_node(self, node: Optional[ShNode]) -> Optional[Component]: def cac_from_component( self, component: Optional[Component] - ) -> Optional[ComponentAttributeClass]: + ) -> Optional[ComponentAttributeClassGt]: return ( self.cacs.get( component.component_attribute_class_id if component is not None else "", diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index 94c8b4b3..d3005aae 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -296,7 +296,7 @@ def pydantic_named_types( # noqa: C901 if excluded_type_names is None: excluded_type_names = DEFAULT_EXCLUDED_TYPE_NAMES if isinstance(module_names, str): - module_names = [module_names] + module_names = [module_names] if module_names else [] if unimported := [ module_name for module_name in module_names if module_name not in sys.modules ]: diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index 23a8f8db..8f79d550 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -4,21 +4,18 @@ import typing from typing import Any, Type, TypeVar -import gwproto.types.fibaro_smart_implant_cac_gt # noqa: F401 +from pydantic import ValidationError + +import gwproto.types.cacs import gwproto.types.fibaro_smart_implant_component_gt # noqa: F401 -import gwproto.types.hubitat_cac_gt # noqa: F401 import gwproto.types.hubitat_component_gt # noqa: F401 -import gwproto.types.hubitat_poller_cac_gt # noqa: F401 import gwproto.types.hubitat_poller_component_gt # noqa: F401 -import gwproto.types.hubitat_tank_cac_gt # noqa: F401 import gwproto.types.hubitat_tank_component_gt # noqa: F401 -import gwproto.types.rest_poller_cac_gt # noqa: F401 import gwproto.types.rest_poller_component_gt # noqa: F401 -import gwproto.types.web_server_cac_gt # noqa: F401 import gwproto.types.web_server_component_gt # noqa: F401 from gwproto.data_classes.component import Component -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.decoders import PydanticTypeNameDecoder +from gwproto.types import ComponentAttributeClassGt T = TypeVar("T") @@ -26,8 +23,9 @@ def decode_to_data_class( decoded_gt: typing.Any, return_type: Type[T], - allow_missing_func: bool = True, # noqa: FBT001, FBT002 - allow_non_instance: bool = False, # noqa: FBT001, FBT002 + *, + allow_missing_func: bool = True, + allow_non_instance: bool = False, ) -> T: if hasattr(decoded_gt, "to_data_class"): data_class = decoded_gt.to_data_class() @@ -52,16 +50,24 @@ def __init__(self, model_name: str, **kwargs: Any) -> None: kwargs["type_name_regex"] = CacDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) - def decode_to_data_class( - self, - data: dict, - allow_missing_func: bool = True, # noqa: FBT001, FBT002 - ) -> ComponentAttributeClass: - return decode_to_data_class( - decoded_gt=self.decode_obj(data), - return_type=ComponentAttributeClass, - allow_missing_func=allow_missing_func, - ) + def decode( + self, d: dict, *, allow_missing: bool = True + ) -> ComponentAttributeClassGt: + try: + decoded = self.loader.model_validate({self.payload_field_name: d}).Payload + if not isinstance(decoded, ComponentAttributeClassGt): + raise TypeError( + f"ERROR. CacDecoder decoded type {type(decoded)}, " + "not ComponentAttributeClassGt" + ) + except ValidationError as e: + if allow_missing and any( + error.get("type") == "union_tag_invalid" for error in e.errors() + ): + decoded = ComponentAttributeClassGt(**d) + else: + raise + return decoded class ComponentDecoder(PydanticTypeNameDecoder): @@ -75,7 +81,8 @@ def __init__(self, model_name: str, **kwargs: Any) -> None: def decode_to_data_class( self, data: dict, - allow_missing_func: bool = True, # noqa: FBT001, FBT002 + *, + allow_missing_func: bool = True, ) -> Component: return decode_to_data_class( decoded_gt=self.decode_obj(data), @@ -86,14 +93,7 @@ def decode_to_data_class( default_cac_decoder = CacDecoder( model_name="DefaultCacDecoder", - module_names=[ - "gwproto.types.fibaro_smart_implant_cac_gt", - "gwproto.types.hubitat_cac_gt", - "gwproto.types.hubitat_poller_cac_gt", - "gwproto.types.hubitat_tank_cac_gt", - "gwproto.types.rest_poller_cac_gt", - "gwproto.types.web_server_cac_gt", - ], + modules=[gwproto.types.cacs], ) default_component_decoder = ComponentDecoder( diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 8d5906a5..2cedbe62 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -2,10 +2,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class ActorClass(StrEnum): +class ActorClass(SymbolizedEnum): """ Determines the code running Spaceheat Nodes supervised by Spaceheat SCADA software diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 782fb752..5f42ce73 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class LocalCommInterface(StrEnum): +class LocalCommInterface(SymbolizedEnum): """ Categorization of in-house comm mechanisms for SCADA diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index 4b58d8c3..ecaf7401 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class MakeModel(StrEnum): +class MakeModel(SymbolizedEnum): """ Determines Make/Model of device associated to a Spaceheat Node supervised by SCADA diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index 79b76adc..d0644632 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class Role(StrEnum): +class Role(SymbolizedEnum): """ Categorizes SpaceheatNodes by their function within the heating system diff --git a/src/gwproto/enums/symbolized.py b/src/gwproto/enums/symbolized.py new file mode 100644 index 00000000..f8490f44 --- /dev/null +++ b/src/gwproto/enums/symbolized.py @@ -0,0 +1,11 @@ +from gwproto.enums.better_str_enum import BetterStrEnum + + +class SymbolizedEnum(BetterStrEnum): + @classmethod + def symbol_to_value(cls, symbol: str) -> str: + raise NotImplementedError + + @classmethod + def value_to_symbol(cls, value: str) -> str: + raise NotImplementedError diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index a1bbba38..fd395993 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class TelemetryName(StrEnum): +class TelemetryName(SymbolizedEnum): """ Specifies the name of sensed data reported by a Spaceheat SCADA diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index 3c806f9a..b1e365bb 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum +from gwproto.enums.symbolized import SymbolizedEnum -class Unit(StrEnum): +class Unit(SymbolizedEnum): """ Specifies the physical unit of sensed data reported by SCADA diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 4bdfa232..d370029b 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -2,7 +2,6 @@ from gwproto.types.component_attribute_class_gt import ( ComponentAttributeClassGt, - ComponentAttributeClassGt_Maker, ) from gwproto.types.component_gt import ComponentGt, ComponentGt_Maker from gwproto.types.data_channel import DataChannel, DataChannel_Maker @@ -13,11 +12,9 @@ ) from gwproto.types.electric_meter_cac_gt import ( ElectricMeterCacGt, - ElectricMeterCacGt_Maker, ) from gwproto.types.fibaro_smart_implant_cac_gt import ( FibaroSmartImplantCacGt, - FibaroSmartImplantCacGt_Maker, ) from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, @@ -58,48 +55,43 @@ ) from gwproto.types.hubitat_poller_cac_gt import ( HubitatPollerCacGt, - HubitatPollerCacGt_Maker, ) from gwproto.types.hubitat_poller_component_gt import ( HubitatPollerComponentGt, HubitatPollerComponentGt_Maker, ) -from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt, HubitatTankCacGt_Maker +from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt from gwproto.types.hubitat_tank_component_gt import ( HubitatTankComponentGt, HubitatTankComponentGt_Maker, ) from gwproto.types.multipurpose_sensor_cac_gt import ( MultipurposeSensorCacGt, - MultipurposeSensorCacGt_Maker, ) from gwproto.types.pipe_flow_sensor_cac_gt import ( PipeFlowSensorCacGt, - PipeFlowSensorCacGt_Maker, ) from gwproto.types.pipe_flow_sensor_component_gt import ( PipeFlowSensorComponentGt, PipeFlowSensorComponentGt_Maker, ) from gwproto.types.power_watts import PowerWatts, PowerWatts_Maker -from gwproto.types.relay_cac_gt import RelayCacGt, RelayCacGt_Maker +from gwproto.types.relay_cac_gt import RelayCacGt from gwproto.types.relay_component_gt import RelayComponentGt, RelayComponentGt_Maker from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, - ResistiveHeaterCacGt_Maker, ) from gwproto.types.resistive_heater_component_gt import ( ResistiveHeaterComponentGt, ResistiveHeaterComponentGt_Maker, ) -from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt, RESTPollerCacGt_Maker +from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt from gwproto.types.rest_poller_component_gt import ( RESTPollerComponentGt, RESTPollerComponentGt_Maker, ) from gwproto.types.simple_temp_sensor_cac_gt import ( SimpleTempSensorCacGt, - SimpleTempSensorCacGt_Maker, ) from gwproto.types.simple_temp_sensor_component_gt import ( SimpleTempSensorComponentGt, @@ -120,7 +112,6 @@ __all__ = [ "ComponentAttributeClassGt", - "ComponentAttributeClassGt_Maker", "ComponentGt", "ComponentGt_Maker", "DataChannel", @@ -130,11 +121,9 @@ "EgaugeRegisterConfig", "EgaugeRegisterConfig_Maker", "ElectricMeterCacGt", - "ElectricMeterCacGt_Maker", # "ElectricMeterComponentGt", # "ElectricMeterComponentGt_Maker", "FibaroSmartImplantCacGt", - "FibaroSmartImplantCacGt_Maker", "FibaroSmartImplantComponentGt", "FibaroSmartImplantComponentGt_Maker", "GtDispatchBoolean", @@ -162,37 +151,29 @@ "HubitatCacGt", "HubitatComponentGt", "HubitatPollerCacGt", - "HubitatPollerCacGt_Maker", "HubitatPollerComponentGt", "HubitatPollerComponentGt_Maker", "HubitatTankCacGt", - "HubitatTankCacGt_Maker", "HubitatTankComponentGt", "HubitatTankComponentGt_Maker", "MultipurposeSensorCacGt", - "MultipurposeSensorCacGt_Maker", # "MultipurposeSensorComponentGt", # "MultipurposeSensorComponentGt_Maker", "PipeFlowSensorCacGt", - "PipeFlowSensorCacGt_Maker", "PipeFlowSensorComponentGt", "PipeFlowSensorComponentGt_Maker", "PowerWatts", "PowerWatts_Maker", "RESTPollerCacGt", - "RESTPollerCacGt_Maker", "RESTPollerComponentGt", "RESTPollerComponentGt_Maker", "RelayCacGt", - "RelayCacGt_Maker", "RelayComponentGt", "RelayComponentGt_Maker", "ResistiveHeaterCacGt", - "ResistiveHeaterCacGt_Maker", "ResistiveHeaterComponentGt", "ResistiveHeaterComponentGt_Maker", "SimpleTempSensorCacGt", - "SimpleTempSensorCacGt_Maker", "SimpleTempSensorComponentGt", "SimpleTempSensorComponentGt_Maker", "SnapshotSpaceheat", diff --git a/src/gwproto/types/cacs.py b/src/gwproto/types/cacs.py new file mode 100644 index 00000000..ee1f16e8 --- /dev/null +++ b/src/gwproto/types/cacs.py @@ -0,0 +1,27 @@ +from gwproto.types.electric_meter_cac_gt import ElectricMeterCacGt +from gwproto.types.fibaro_smart_implant_cac_gt import FibaroSmartImplantCacGt +from gwproto.types.hubitat_cac_gt import HubitatCacGt +from gwproto.types.hubitat_poller_cac_gt import HubitatPollerCacGt +from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt +from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt +from gwproto.types.pipe_flow_sensor_cac_gt import PipeFlowSensorCacGt +from gwproto.types.relay_cac_gt import RelayCacGt +from gwproto.types.resistive_heater_cac_gt import ResistiveHeaterCacGt +from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt +from gwproto.types.simple_temp_sensor_cac_gt import SimpleTempSensorCacGt +from gwproto.types.web_server_cac_gt import WebServerCacGt + +__all__ = [ + "ElectricMeterCacGt", + "FibaroSmartImplantCacGt", + "HubitatCacGt", + "HubitatPollerCacGt", + "HubitatTankCacGt", + "MultipurposeSensorCacGt", + "PipeFlowSensorCacGt", + "RESTPollerCacGt", + "RelayCacGt", + "ResistiveHeaterCacGt", + "SimpleTempSensorCacGt", + "WebServerCacGt", +] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index ed522bda..dd96a865 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -1,250 +1,16 @@ """Type component.attribute.class.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.enums import MakeModel +from gwproto.utils import UUID4Str class ComponentAttributeClassGt(BaseModel): - """ - Component Attribute Class Gt. - - Authority for the attributes of the component.attribute.class.gt.000 belongs to the WorldRegistry. - The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational - device data. Generally speaking, a component attribute class is meant to specify WHAT you - might order from a plumbing supply store to 'get the same part.' The Component refers to - something that will have a specific serial number. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "This identifier is used to associate a make/model with a specific component (i.e. " - "the component will point to its ComponentAttributeClassId)." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description=( - "Optional Mutable field to include manufacturer's model name. Note that several different " - "models may be given the same spaceheat.make.model enum name." - ), - default=None, - ) + ComponentAttributeClassId: UUID4Str + DisplayName: Optional[str] = None + MakeModel: MakeModel TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - component.attribute.class.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - component.attribute.class.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"}, - by_alias=True, - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the component.attribute.class.gt.000 representation. - - Instances in the class are python-native representations of component.attribute.class.gt.000 - objects, while the actual component.attribute.class.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ComponentAttributeClassGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ComponentAttributeClassGt_Maker: - type_name = "component.attribute.class.gt" - version = "000" - - def __init__( - self, - component_attribute_class_id: str, - display_name: Optional[str], - ) -> None: - self.tuple = ComponentAttributeClassGt( - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - ) - - @classmethod - def tuple_to_type(cls, tuple_: ComponentAttributeClassGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tuple_.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ComponentAttributeClassGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentAttributeClassGt: - """ - Deserialize a dictionary representation of a component.attribute.class.gt.000 message object - into a ComponentAttributeClassGt python object for internal use. - - This is the near-inverse of the ComponentAttributeClassGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ComponentAttributeClassGt object. - - Returns: - ComponentAttributeClassGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret component.attribute.class.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ComponentAttributeClassGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ComponentAttributeClassGt) -> ComponentAttributeClass: - if t.ComponentAttributeClassId in ComponentAttributeClass.by_id: - dc = ComponentAttributeClass.by_id[t.ComponentAttributeClassId] - else: - dc = ComponentAttributeClass( - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ComponentAttributeClass) -> ComponentAttributeClassGt: - return ComponentAttributeClassGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ComponentAttributeClass: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: ComponentAttributeClass) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ComponentAttributeClass: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 0cdcef45..63c44d45 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -1,337 +1,14 @@ """Type electric.meter.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, List, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator - -from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.enums import LocalCommInterface, TelemetryName -from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class ElectricMeterCacGt(BaseModel): - """ - Type for tracking Electric Meter ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. +from gwproto.types import ComponentAttributeClassGt - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - description=( - "The brand name identifier for the electric meter (what you would specify in order " - "to buy one)." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description="Sample: EGauge 4030", - default=None, - ) - TelemetryNameList: List[TelemetryName] = Field( - title="TelemetryNames read by this power meter", - ) - PollPeriodMs: int = Field( - title="Poll Period in Milliseconds", - description=( - "Poll Period refers to the period of time between two readings by the local actor. " - "This is in contrast to Capture Period, which refers to the period between readings " - "that are sent up to the cloud (or otherwise saved for the long-term)." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/data-polling-capturing-transmitting.rst)" - ), - ) - Interface: LocalCommInterface = Field( - title="Interface", - ) - DefaultBaud: Optional[int] = Field( - title="To be used when the comms method requires a baud rate", - default=None, - ) +class ElectricMeterCacGt(ComponentAttributeClassGt): + TelemetryNameList: list[TelemetryName] + PollPeriodMs: int + Interface: LocalCommInterface + DefaultBaud: Optional[int] = None TypeName: Literal["electric.meter.cac.gt"] = "electric.meter.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - electric.meter.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - electric.meter.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - del d["TelemetryNameList"] - d["TelemetryNameList"] = [ - TelemetryName.value_to_symbol(str(elt.value)) - for elt in self.TelemetryNameList - ] - del d["Interface"] - d["InterfaceGtEnumSymbol"] = LocalCommInterface.value_to_symbol(self.Interface) - return d - - def as_type(self) -> bytes: - """ - Serialize to the electric.meter.cac.gt.000 representation. - - Instances in the class are python-native representations of electric.meter.cac.gt.000 - objects, while the actual electric.meter.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ElectricMeterCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ElectricMeterCacGt_Maker: - type_name = "electric.meter.cac.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - display_name: Optional[str], - telemetry_name_list: List[TelemetryName], - poll_period_ms: int, - interface: LocalCommInterface, - default_baud: Optional[int], - ) -> None: - self.tuple = ElectricMeterCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - DisplayName=display_name, - TelemetryNameList=telemetry_name_list, - PollPeriodMs=poll_period_ms, - Interface=interface, - DefaultBaud=default_baud, - ) - - @classmethod - def tuple_to_type(cls, tpl: ElectricMeterCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ElectricMeterCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: # noqa: C901 - """ - Deserialize a dictionary representation of a electric.meter.cac.gt.000 message object - into a ElectricMeterCacGt python object for internal use. - - This is the near-inverse of the ElectricMeterCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ElectricMeterCacGt object. - - Returns: - ElectricMeterCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "TelemetryNameList" not in d2: - raise SchemaError(f"dict <{d2}> missing TelemetryNameList") - if not isinstance(d2["TelemetryNameList"], List): - raise SchemaError("TelemetryNameList must be a List!") - telemetry_name_list = [] - for elt in d2["TelemetryNameList"]: - value = TelemetryName.symbol_to_value(elt) - telemetry_name_list.append(TelemetryName(value)) - d2["TelemetryNameList"] = telemetry_name_list - if "PollPeriodMs" not in d2: - raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") - if "InterfaceGtEnumSymbol" not in d2: - raise SchemaError(f"InterfaceGtEnumSymbol missing from dict <{d2}>") - value = LocalCommInterface.symbol_to_value(d2["InterfaceGtEnumSymbol"]) - d2["Interface"] = LocalCommInterface(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret electric.meter.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ElectricMeterCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ElectricMeterCacGt) -> ElectricMeterCac: - if t.ComponentAttributeClassId in ElectricMeterCac.by_id: - dc = ElectricMeterCac.by_id[t.ComponentAttributeClassId] - else: - dc = ElectricMeterCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - display_name=t.DisplayName, - telemetry_name_list=t.TelemetryNameList, - poll_period_ms=t.PollPeriodMs, - interface=t.Interface, - default_baud=t.DefaultBaud, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ElectricMeterCac) -> ElectricMeterCacGt: - return ElectricMeterCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - display_name=dc.display_name, - telemetry_name_list=dc.telemetry_name_list, - poll_period_ms=dc.poll_period_ms, - interface=dc.interface, - default_baud=dc.default_baud, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ElectricMeterCac: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) - - @classmethod - def dc_to_type(cls, dc: ElectricMeterCac) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ElectricMeterCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index e7c95d64..c9106df3 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -1,76 +1,11 @@ -import json -import typing -from typing import Any, Literal +from typing import Literal from pydantic import ConfigDict -from gwproto.data_classes.cacs.fibaro_smart_implant_cac import FibaroSmartImplantCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types import ComponentAttributeClassGt class FibaroSmartImplantCacGt(ComponentAttributeClassGt): Model: str = "" TypeName: Literal["fibaro.smart.implant.cac.gt"] = "fibaro.smart.implant.cac.gt" - Version: Literal["000"] = "000" model_config = ConfigDict(extra="allow") - - @classmethod - def from_data_class(cls, cac: FibaroSmartImplantCac) -> "FibaroSmartImplantCacGt": - return FibaroSmartImplantCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> FibaroSmartImplantCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(FibaroSmartImplantCac, cac) - return FibaroSmartImplantCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - -class FibaroSmartImplantCacGt_Maker: - type_name: str = FibaroSmartImplantCacGt.model_fields["TypeName"].default - version = "000" - tuple: FibaroSmartImplantCacGt - - def __init__(self, cac: FibaroSmartImplantCac) -> None: - self.tuple = FibaroSmartImplantCacGt.from_data_class(cac) - - @classmethod - def tuple_to_type(cls, tpl: FibaroSmartImplantCacGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> FibaroSmartImplantCacGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> FibaroSmartImplantCacGt: - return FibaroSmartImplantCacGt(**d) - - @classmethod - def tuple_to_dc(cls, t: FibaroSmartImplantCacGt) -> FibaroSmartImplantCac: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: FibaroSmartImplantCac) -> FibaroSmartImplantCacGt: - return FibaroSmartImplantCacGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> FibaroSmartImplantCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: FibaroSmartImplantCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> FibaroSmartImplantCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index b03f14b7..b79d643a 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -1,30 +1,7 @@ -import typing from typing import Literal -from gwproto.data_classes.cacs.hubitat_cac import HubitatCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.cac.gt"] = "hubitat.cac.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, cac: HubitatCac) -> "HubitatCacGt": - return HubitatCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> HubitatCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(HubitatCac, cac) - return HubitatCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index 1032539c..538067b9 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -1,36 +1,7 @@ -import typing from typing import Literal -from gwproto.data_classes.cacs.hubitat_cac import HubitatCac -from gwproto.data_classes.cacs.hubitat_poller_cac import HubitatPollerCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatPollerCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.poller.cac.gt"] = "hubitat.poller.cac.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, cac: HubitatCac) -> "HubitatPollerCacGt": - return HubitatPollerCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> HubitatPollerCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(HubitatPollerCac, cac) - return HubitatPollerCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - -class HubitatPollerCacGt_Maker: - type_name = "hubitat.poller.cac.gt" - version = "000" diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index d238d3ef..cc58132b 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -1,72 +1,7 @@ -import json -import typing from typing import Literal -from gwproto.data_classes.cacs.hubitat_tank_module_cac import HubitatTankModuleCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatTankCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.tank.cac.gt"] = "hubitat.tank.cac.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, cac: HubitatTankModuleCac) -> "HubitatTankCacGt": - return HubitatTankCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> HubitatTankModuleCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(HubitatTankModuleCac, cac) - return HubitatTankModuleCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - -class HubitatTankCacGt_Maker: - type_name: str = HubitatTankCacGt.model_fields["TypeName"].default - version = "000" - tuple: HubitatTankCacGt - - def __init__(self, cac: HubitatTankModuleCac) -> None: - self.tuple = HubitatTankCacGt.from_data_class(cac) - - @classmethod - def tuple_to_type(cls, tpl: HubitatTankCacGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> HubitatTankCacGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, typing.Any]) -> HubitatTankCacGt: - return HubitatTankCacGt(**d) - - @classmethod - def tuple_to_dc(cls, t: HubitatTankCacGt) -> HubitatTankModuleCac: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: HubitatTankModuleCac) -> HubitatTankCacGt: - return HubitatTankCacGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> HubitatTankModuleCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: HubitatTankModuleCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[typing.Any, str]) -> HubitatTankModuleCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index 4989e5a4..a854a097 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -1,340 +1,17 @@ """Type multipurpose.sensor.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, List, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator - -from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac -from gwproto.enums import MakeModel as EnumMakeModel from gwproto.enums import TelemetryName, Unit -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class MultipurposeSensorCacGt(BaseModel): - """ - Type for tracking Multipuprose Sensor ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. +from gwproto.types import ComponentAttributeClassGt - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - description=( - "Meant to be enough to articulate any difference in how GridWorks code would interact " - "with a device. Should be able to use this information to buy or build a device." - ), - ) - PollPeriodMs: int = Field( - title="Poll Period in Milliseconds", - description=( - "Poll Period refers to the period of time between two readings by the local actor. " - "This is in contrast to Capture Period, which refers to the period between readings " - "that are sent up to the cloud (or otherwise saved for the long-term)." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/data-polling-capturing-transmitting.rst)" - ), - ) - Exponent: int = Field( - title="Exponent", - description=( - "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " - "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " - "would indicate 65.3 deg C" - ), - ) - TempUnit: Unit = Field( - title="Temp Unit", - ) - TelemetryNameList: List[TelemetryName] = Field( - title="TelemetryNameList", - ) - MaxThermistors: Optional[int] = Field( - title="MaxThermistors", - description="The maximum number of temperature sensors this multipurpose sensor can read.", - default=None, - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description="Sample: GridWorks TSnap1.0 as 12-channel analog temp sensor", - default=None, - ) - CommsMethod: Optional[str] = Field( - title="CommsMethod", - default=None, - ) +class MultipurposeSensorCacGt(ComponentAttributeClassGt): + PollPeriodMs: int + Exponent: int + TempUnit: Unit + TelemetryNameList: list[TelemetryName] + MaxThermistors: Optional[int] = None + DisplayName: Optional[str] = None + CommsMethod: Optional[str] = None TypeName: Literal["multipurpose.sensor.cac.gt"] = "multipurpose.sensor.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - multipurpose.sensor.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - multipurpose.sensor.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - del d["TempUnit"] - d["TempUnitGtEnumSymbol"] = Unit.value_to_symbol(self.TempUnit) - d["TelemetryNameList"] = [ - TelemetryName.value_to_symbol(elt) for elt in self.TelemetryNameList - ] - return d - - def as_type(self) -> bytes: - """ - Serialize to the multipurpose.sensor.cac.gt.000 representation. - - Instances in the class are python-native representations of multipurpose.sensor.cac.gt.000 - objects, while the actual multipurpose.sensor.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is MultipurposeSensorCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class MultipurposeSensorCacGt_Maker: - type_name = "multipurpose.sensor.cac.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - poll_period_ms: int, - exponent: int, - temp_unit: Unit, - telemetry_name_list: List[TelemetryName], - max_thermistors: Optional[int], - display_name: Optional[str], - comms_method: Optional[str], - ) -> None: - self.tuple = MultipurposeSensorCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - PollPeriodMs=poll_period_ms, - Exponent=exponent, - TempUnit=temp_unit, - TelemetryNameList=telemetry_name_list, - MaxThermistors=max_thermistors, - DisplayName=display_name, - CommsMethod=comms_method, - ) - - @classmethod - def tuple_to_type(cls, tpl: MultipurposeSensorCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> MultipurposeSensorCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: # noqa: C901 - """ - Deserialize a dictionary representation of a multipurpose.sensor.cac.gt.000 message object - into a MultipurposeSensorCacGt python object for internal use. - - This is the near-inverse of the MultipurposeSensorCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a MultipurposeSensorCacGt object. - - Returns: - MultipurposeSensorCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "PollPeriodMs" not in d2: - raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") - if "Exponent" not in d2: - raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TempUnitGtEnumSymbol" not in d2: - raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") - value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) - d2["TempUnit"] = Unit(value) - if "TelemetryNameList" not in d2: - raise SchemaError(f"dict <{d2}> missing TelemetryNameList") - if not isinstance(d2["TelemetryNameList"], List): - raise SchemaError("TelemetryNameList must be a List!") - telemetry_name_list = [] - for elt in d2["TelemetryNameList"]: - value = TelemetryName.symbol_to_value(elt) - telemetry_name_list.append(TelemetryName(value)) - d2["TelemetryNameList"] = telemetry_name_list - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret multipurpose.sensor.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return MultipurposeSensorCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: MultipurposeSensorCacGt) -> MultipurposeSensorCac: - if t.ComponentAttributeClassId in MultipurposeSensorCac.by_id: - dc = MultipurposeSensorCac.by_id[t.ComponentAttributeClassId] - else: - dc = MultipurposeSensorCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - poll_period_ms=t.PollPeriodMs, - exponent=t.Exponent, - temp_unit=t.TempUnit, - telemetry_name_list=t.TelemetryNameList, - max_thermistors=t.MaxThermistors, - display_name=t.DisplayName, - comms_method=t.CommsMethod, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: MultipurposeSensorCac) -> MultipurposeSensorCacGt: - return MultipurposeSensorCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - poll_period_ms=dc.poll_period_ms, - exponent=dc.exponent, - temp_unit=dc.temp_unit, - telemetry_name_list=dc.telemetry_name_list, - max_thermistors=dc.max_thermistors, - display_name=dc.display_name, - comms_method=dc.comms_method, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> MultipurposeSensorCac: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) - - @classmethod - def dc_to_type(cls, dc: MultipurposeSensorCac) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> MultipurposeSensorCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index 959ebe41..e933937f 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -1,265 +1,10 @@ """Type pipe.flow.sensor.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentAttributeClassGt -from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac -from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class PipeFlowSensorCacGt(BaseModel): - """ - Type for tracking Pipe Flow Sensor ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description="Sample: Atlas Scientific EZO FLO i2c", - default=None, - ) - CommsMethod: Optional[str] = Field( - title="CommsMethod", - default=None, - ) +class PipeFlowSensorCacGt(ComponentAttributeClassGt): + CommsMethod: Optional[str] = None TypeName: Literal["pipe.flow.sensor.cac.gt"] = "pipe.flow.sensor.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - pipe.flow.sensor.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - pipe.flow.sensor.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - return d - - def as_type(self) -> bytes: - """ - Serialize to the pipe.flow.sensor.cac.gt.000 representation. - - Instances in the class are python-native representations of pipe.flow.sensor.cac.gt.000 - objects, while the actual pipe.flow.sensor.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is PipeFlowSensorCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class PipeFlowSensorCacGt_Maker: - type_name = "pipe.flow.sensor.cac.gt" - version = "000" - - def __init__( - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - display_name: Optional[str], - comms_method: Optional[str], - ) -> None: - self.tuple = PipeFlowSensorCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - DisplayName=display_name, - CommsMethod=comms_method, - ) - - @classmethod - def tuple_to_type(cls, tpl: PipeFlowSensorCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> PipeFlowSensorCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorCacGt: - """ - Deserialize a dictionary representation of a pipe.flow.sensor.cac.gt.000 message object - into a PipeFlowSensorCacGt python object for internal use. - - This is the near-inverse of the PipeFlowSensorCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a PipeFlowSensorCacGt object. - - Returns: - PipeFlowSensorCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret pipe.flow.sensor.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return PipeFlowSensorCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: PipeFlowSensorCacGt) -> PipeFlowSensorCac: - if t.ComponentAttributeClassId in PipeFlowSensorCac.by_id: - dc = PipeFlowSensorCac.by_id[t.ComponentAttributeClassId] - else: - dc = PipeFlowSensorCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - display_name=t.DisplayName, - comms_method=t.CommsMethod, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: PipeFlowSensorCac) -> PipeFlowSensorCacGt: - return PipeFlowSensorCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - display_name=dc.display_name, - comms_method=dc.comms_method, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> PipeFlowSensorCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: PipeFlowSensorCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> PipeFlowSensorCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 27e8b012..3c61d98a 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -1,265 +1,10 @@ """Type relay.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentAttributeClassGt -from gwproto.data_classes.cacs.relay_cac import RelayCac -from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class RelayCacGt(BaseModel): - """ - Type for tracking Relay ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - TypicalResponseTimeMs: int = Field( - title="TypicalResponseTimeMs", - ) +class RelayCacGt(ComponentAttributeClassGt): + TypicalResponseTimeMs: int TypeName: Literal["relay.cac.gt"] = "relay.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - relay.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - relay.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - return d - - def as_type(self) -> bytes: - """ - Serialize to the relay.cac.gt.000 representation. - - Instances in the class are python-native representations of relay.cac.gt.000 - objects, while the actual relay.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is RelayCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class RelayCacGt_Maker: - type_name = "relay.cac.gt" - version = "000" - - def __init__( - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - display_name: Optional[str], - typical_response_time_ms: int, - ) -> None: - self.tuple = RelayCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - DisplayName=display_name, - TypicalResponseTimeMs=typical_response_time_ms, - ) - - @classmethod - def tuple_to_type(cls, tpl: RelayCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> RelayCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> RelayCacGt: - """ - Deserialize a dictionary representation of a relay.cac.gt.000 message object - into a RelayCacGt python object for internal use. - - This is the near-inverse of the RelayCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a RelayCacGt object. - - Returns: - RelayCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "TypicalResponseTimeMs" not in d2: - raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret relay.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return RelayCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: RelayCacGt) -> RelayCac: - if t.ComponentAttributeClassId in RelayCac.by_id: - dc = RelayCac.by_id[t.ComponentAttributeClassId] - else: - dc = RelayCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - display_name=t.DisplayName, - typical_response_time_ms=t.TypicalResponseTimeMs, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: RelayCac) -> RelayCacGt: - return RelayCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - display_name=dc.display_name, - typical_response_time_ms=dc.typical_response_time_ms, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> RelayCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: RelayCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> RelayCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 0d177037..8df08fa7 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -1,303 +1,11 @@ """Type resistive.heater.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentAttributeClassGt -from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac -from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class ResistiveHeaterCacGt(BaseModel): - """ - Type for tracking Resistive Heater ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - NameplateMaxPowerW: int = Field( - title="NameplateMaxPowerW", - ) - RatedVoltageV: int = Field( - title="RatedVoltageV", - ) +class ResistiveHeaterCacGt(ComponentAttributeClassGt): + NameplateMaxPowerW: int + RatedVoltageV: int TypeName: Literal["resistive.heater.cac.gt"] = "resistive.heater.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("RatedVoltageV") - @classmethod - def _check_rated_voltage_v(cls, v: int) -> int: - try: - check_is_positive_integer(v) - except ValueError as e: - raise ValueError( - f"RatedVoltageV failed PositiveInteger format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - resistive.heater.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - resistive.heater.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - return d - - def as_type(self) -> bytes: - """ - Serialize to the resistive.heater.cac.gt.000 representation. - - Instances in the class are python-native representations of resistive.heater.cac.gt.000 - objects, while the actual resistive.heater.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ResistiveHeaterCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ResistiveHeaterCacGt_Maker: - type_name = "resistive.heater.cac.gt" - version = "000" - - def __init__( - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - display_name: Optional[str], - nameplate_max_power_w: int, - rated_voltage_v: int, - ) -> None: - self.tuple = ResistiveHeaterCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - DisplayName=display_name, - NameplateMaxPowerW=nameplate_max_power_w, - RatedVoltageV=rated_voltage_v, - ) - - @classmethod - def tuple_to_type(cls, tpl: ResistiveHeaterCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ResistiveHeaterCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterCacGt: - """ - Deserialize a dictionary representation of a resistive.heater.cac.gt.000 message object - into a ResistiveHeaterCacGt python object for internal use. - - This is the near-inverse of the ResistiveHeaterCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ResistiveHeaterCacGt object. - - Returns: - ResistiveHeaterCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "NameplateMaxPowerW" not in d2: - raise SchemaError(f"dict missing NameplateMaxPowerW: <{d2}>") - if "RatedVoltageV" not in d2: - raise SchemaError(f"dict missing RatedVoltageV: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret resistive.heater.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ResistiveHeaterCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ResistiveHeaterCacGt) -> ResistiveHeaterCac: - if t.ComponentAttributeClassId in ResistiveHeaterCac.by_id: - dc = ResistiveHeaterCac.by_id[t.ComponentAttributeClassId] - else: - dc = ResistiveHeaterCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - display_name=t.DisplayName, - nameplate_max_power_w=t.NameplateMaxPowerW, - rated_voltage_v=t.RatedVoltageV, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ResistiveHeaterCac) -> ResistiveHeaterCacGt: - return ResistiveHeaterCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - display_name=dc.display_name, - nameplate_max_power_w=dc.nameplate_max_power_w, - rated_voltage_v=dc.rated_voltage_v, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ResistiveHeaterCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: ResistiveHeaterCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ResistiveHeaterCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index bca855f1..ad642855 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -1,72 +1,7 @@ -import json -import typing -from typing import Any, Literal +from typing import Literal -from gwproto.data_classes.cacs.rest_poller_cac import RESTPollerCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types import ComponentAttributeClassGt class RESTPollerCacGt(ComponentAttributeClassGt): TypeName: Literal["rest.poller.cac.gt"] = "rest.poller.cac.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, cac: RESTPollerCac) -> "RESTPollerCacGt": - return RESTPollerCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> RESTPollerCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(RESTPollerCac, cac) - return RESTPollerCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - -class RESTPollerCacGt_Maker: - type_name: str = RESTPollerCacGt.model_fields["TypeName"].default - version = "000" - tuple: RESTPollerCacGt - - def __init__(self, cac: RESTPollerCac) -> None: - self.tuple = RESTPollerCacGt.from_data_class(cac) - - @classmethod - def tuple_to_type(cls, tpl: RESTPollerCacGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> RESTPollerCacGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> RESTPollerCacGt: - return RESTPollerCacGt(**d) - - @classmethod - def tuple_to_dc(cls, t: RESTPollerCacGt) -> RESTPollerCac: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: RESTPollerCac) -> RESTPollerCacGt: - return RESTPollerCacGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> RESTPollerCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: RESTPollerCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 6af6864b..47159c72 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -1,317 +1,15 @@ """Type simple.temp.sensor.cac.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.enums import TelemetryName, Unit +from gwproto.types import ComponentAttributeClassGt -from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac -from gwproto.enums import MakeModel as EnumMakeModel -from gwproto.enums import TelemetryName as EnumTelemetryName -from gwproto.enums import Unit -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class SimpleTempSensorCacGt(BaseModel): - """ - Type for tracking Simple Temp Sensor ComponentAttributeClasses. - - GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for - managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) - """ - - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " - "Authority is maintained by the World Registry." - ), - ) - MakeModel: EnumMakeModel = Field( - title="MakeModel", - ) - TypicalResponseTimeMs: int = Field( - title="TypicalResponseTimeMs", - ) - Exponent: int = Field( - title="Exponent", - description=( - "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " - "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " - "would indicate 65.3 deg C" - ), - ) - TempUnit: Unit = Field( - title="TempUnit", - ) - TelemetryName: EnumTelemetryName = Field( - title="TelemetryName", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - CommsMethod: Optional[str] = Field( - title="CommsMethod", - default=None, - ) +class SimpleTempSensorCacGt(ComponentAttributeClassGt): + TypicalResponseTimeMs: int + Exponent: int + TempUnit: Unit + TelemetryName: TelemetryName + CommsMethod: Optional[str] = None TypeName: Literal["simple.temp.sensor.cac.gt"] = "simple.temp.sensor.cac.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - simple.temp.sensor.cac.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - simple.temp.sensor.cac.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["MakeModel"] - d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) - del d["TempUnit"] - d["TempUnitGtEnumSymbol"] = Unit.value_to_symbol(self.TempUnit) - del d["TelemetryName"] - d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( - self.TelemetryName - ) - return d - - def as_type(self) -> bytes: - """ - Serialize to the simple.temp.sensor.cac.gt.000 representation. - - Instances in the class are python-native representations of simple.temp.sensor.cac.gt.000 - objects, while the actual simple.temp.sensor.cac.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is SimpleTempSensorCacGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class SimpleTempSensorCacGt_Maker: - type_name = "simple.temp.sensor.cac.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - make_model: EnumMakeModel, - typical_response_time_ms: int, - exponent: int, - temp_unit: Unit, - telemetry_name: EnumTelemetryName, - display_name: Optional[str], - comms_method: Optional[str], - ) -> None: - self.tuple = SimpleTempSensorCacGt( - ComponentAttributeClassId=component_attribute_class_id, - MakeModel=make_model, - TypicalResponseTimeMs=typical_response_time_ms, - Exponent=exponent, - TempUnit=temp_unit, - TelemetryName=telemetry_name, - DisplayName=display_name, - CommsMethod=comms_method, - ) - - @classmethod - def tuple_to_type(cls, tpl: SimpleTempSensorCacGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> SimpleTempSensorCacGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorCacGt: - """ - Deserialize a dictionary representation of a simple.temp.sensor.cac.gt.000 message object - into a SimpleTempSensorCacGt python object for internal use. - - This is the near-inverse of the SimpleTempSensorCacGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a SimpleTempSensorCacGt object. - - Returns: - SimpleTempSensorCacGt - """ - d2 = dict(d) - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") - if "MakeModelGtEnumSymbol" not in d2: - raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") - value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) - d2["MakeModel"] = EnumMakeModel(value) - if "TypicalResponseTimeMs" not in d2: - raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") - if "Exponent" not in d2: - raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TempUnitGtEnumSymbol" not in d2: - raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") - value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) - d2["TempUnit"] = Unit(value) - if "TelemetryNameGtEnumSymbol" not in d2: - raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") - value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) - d2["TelemetryName"] = EnumTelemetryName(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret simple.temp.sensor.cac.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return SimpleTempSensorCacGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: SimpleTempSensorCacGt) -> SimpleTempSensorCac: - if t.ComponentAttributeClassId in SimpleTempSensorCac.by_id: - dc = SimpleTempSensorCac.by_id[t.ComponentAttributeClassId] - else: - dc = SimpleTempSensorCac( - component_attribute_class_id=t.ComponentAttributeClassId, - make_model=t.MakeModel, - typical_response_time_ms=t.TypicalResponseTimeMs, - exponent=t.Exponent, - temp_unit=t.TempUnit, - telemetry_name=t.TelemetryName, - display_name=t.DisplayName, - comms_method=t.CommsMethod, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: SimpleTempSensorCac) -> SimpleTempSensorCacGt: - return SimpleTempSensorCacGt_Maker( - component_attribute_class_id=dc.component_attribute_class_id, - make_model=dc.make_model, - typical_response_time_ms=dc.typical_response_time_ms, - exponent=dc.exponent, - temp_unit=dc.temp_unit, - telemetry_name=dc.telemetry_name, - display_name=dc.display_name, - comms_method=dc.comms_method, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> SimpleTempSensorCac: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: SimpleTempSensorCac) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> SimpleTempSensorCac: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index ddc17579..be1674d1 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -1,30 +1,7 @@ -import typing from typing import Literal -from gwproto.data_classes.cacs.web_server_cac import WebServerCac -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class WebServerCacGt(ComponentAttributeClassGt): TypeName: Literal["web.server.cac.gt"] = "web.server.cac.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, cac: WebServerCac) -> "WebServerCacGt": - return WebServerCacGt( - ComponentAttributeClassId=cac.component_attribute_class_id, - DisplayName=cac.display_name, - ) - - def to_data_class(self) -> WebServerCac: - cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) - if cac is not None: - return typing.cast(WebServerCac, cac) - return WebServerCac( - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - ) - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index eb2c18c2..4306e4de 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -1,4 +1,8 @@ import re +import uuid +from typing import Annotated + +from pydantic import BeforeValidator snake_add_underscore_to_camel_pattern = re.compile(r"(? str: def has_mac_address_format(mac_str: str) -> bool: return bool(MAC_REGEX.match(mac_str.lower())) + + +def str_is_valid_uuid4(v: str) -> str: + v = str(v) + try: + u = uuid.UUID(v) + except Exception as e: + raise ValueError(f"Invalid UUID4: {v} <{e}>") from e + if u.version != 4: + raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") + return str(u) + + +UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] diff --git a/tests/cac_load_utils.py b/tests/cac_load_utils.py new file mode 100644 index 00000000..57ddc227 --- /dev/null +++ b/tests/cac_load_utils.py @@ -0,0 +1,115 @@ +from dataclasses import dataclass, field +from typing import Optional, Type + +from gwproto import CacDecoder, default_cac_decoder +from gwproto.data_classes.hardware_layout import ( + load_cacs, +) +from gwproto.types import ComponentAttributeClassGt + + +@dataclass +class CacCase: + tag: str + src_cac: ComponentAttributeClassGt | dict + exp_cac_type: Optional[Type] = ComponentAttributeClassGt + exp_cac: Optional[ComponentAttributeClassGt | dict] = None + exp_exceptions: list[Type[Exception]] = field(default_factory=list) + + +@dataclass +class CacCaseError: + case_idx: int + case: CacCase + + def __str__(self) -> str: + return f"{self.case.tag:30s} {self.case_idx:2d} {type(self)}" + + +@dataclass +class CacLoadError(CacCaseError): + exception: Exception + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\t{type(self.exception)}" + f"\n\t\t{self.exception}" + ) + + +@dataclass +class CacMatchError(CacCaseError): + exp_cac: ComponentAttributeClassGt | dict + loaded_cac: ComponentAttributeClassGt + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\texp: {type(self.exp_cac)}" + f"\n\t\tgot: {type(self.loaded_cac)}" + ) + + +@dataclass +class CacLoadResult: + ok: bool + loaded: ComponentAttributeClassGt | None + exception: Exception | None + + +def _decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: + if decoder is None: + decoder = default_cac_decoder + cac_dict = ( + case.src_cac.model_dump() + if isinstance(case.src_cac, ComponentAttributeClassGt) + else case.src_cac + ) + cac_id = cac_dict["ComponentAttributeClassId"] + try: + loaded_cac = load_cacs( + layout={"OtherCacs": [cac_dict]}, + raise_errors=True, + cac_decoder=decoder, + )[cac_id] + exception = None + except Exception as e: # noqa: BLE001 + loaded_cac = None + exception = e + if loaded_cac is None: + ok = type(exception) in case.exp_exceptions + else: + ok = not case.exp_exceptions + return CacLoadResult(ok, loaded_cac, exception) + + +def assert_cac_load(cases: list[CacCase], decoder: Optional[CacDecoder] = None) -> None: + errors: list[CacCaseError] = [] + for case_idx, case in enumerate(cases): + load_result = _decode_cac(case, decoder) + if not load_result.ok: + errors.append(CacLoadError(case_idx, case, load_result.exception)) + elif not case.exp_exceptions: + exp_cac = case.src_cac if case.exp_cac is None else case.exp_cac + if isinstance(exp_cac, dict): + exp_cac = case.exp_cac_type(**exp_cac) + if load_result.loaded != exp_cac: + errors.append( + CacMatchError( + case_idx=case_idx, + case=case, + exp_cac=exp_cac, + loaded_cac=load_result.loaded, + ) + ) + if errors: + err_str = "ERROR. Got cac load/matching errors:" + first_exception = None + for error in errors: + err_str += f"\n\t{error}" + if first_exception is None and hasattr(error, "exception"): + first_exception = error.exception + if first_exception is not None: + raise ValueError(err_str) from first_exception + raise ValueError(err_str) diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 9bc51006..3bf6f003 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -1,26 +1,26 @@ -from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac -from gwproto.data_classes.hardware_layout import HardwareLayout -from gwproto.types import ElectricMeterCacGt_Maker - -# Running the below disrupts other tests. Need to set up the -# test isolation as per scada - - -def test_electric_meter_cac() -> None: - HardwareLayout.load("tests/config/hardware-layout.json") - d = { - "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "MakeModelGtEnumSymbol": "076da322", - "DisplayName": "Gridworks Pm1 Simulated Power Meter", - "InterfaceGtEnumSymbol": "efc144cd", - "PollPeriodMs": 1000, - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - - gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) - assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id - dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] - - assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" +# from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac +# from gwproto.data_classes.hardware_layout import HardwareLayout +# from gwproto.types import ElectricMeterCacGt_Maker +# +# # Running the below disrupts other tests. Need to set up the +# # test isolation as per scada +# +# +# def test_electric_meter_cac() -> None: +# HardwareLayout.load("tests/config/hardware-layout.json") +# d = { +# "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", +# "MakeModelGtEnumSymbol": "076da322", +# "DisplayName": "Gridworks Pm1 Simulated Power Meter", +# "InterfaceGtEnumSymbol": "efc144cd", +# "PollPeriodMs": 1000, +# "TelemetryNameList": ["af39eec9"], +# "TypeName": "electric.meter.cac.gt", +# "Version": "000", +# } +# +# gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) +# assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id +# dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] +# +# assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" diff --git a/tests/data_classes/test_electric_meter_component.py b/tests/data_classes/test_electric_meter_component.py index 126c9837..8e363020 100644 --- a/tests/data_classes/test_electric_meter_component.py +++ b/tests/data_classes/test_electric_meter_component.py @@ -9,7 +9,10 @@ def test_electric_meter_component() -> None: - HardwareLayout.load("tests/config/hardware-layout.json") + errors = [] + HardwareLayout.load( + "tests/config/hardware-layout.json", errors=errors, raise_errors=False + ) d = { "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 1e95bad6..7726c584 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -2,7 +2,7 @@ from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode -from gwproto.types import ElectricMeterCacGt_Maker, SpaceheatNodeGt_Maker +from gwproto.types import SpaceheatNodeGt_Maker from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker from tests.utils import flush_all @@ -12,17 +12,6 @@ def test_flush_and_load_house() -> None: load_house() successfully loads test objects""" flush_all() - unknown_electric_meter_cac_dict = { - "ComponentAttributeClassId": "c1f17330-6269-4bc5-aa4b-82e939e9b70c", - "MakeModelGtEnumSymbol": "00000000", - "DisplayName": "Unknown Power Meter", - "PollPeriodMs": 1000, - "InterfaceGtEnumSymbol": "00000000", - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - electric_meter_component_dict = { "ComponentId": "c7d352db-9a86-40f0-9601-d99243719cc5", "DisplayName": "Test unknown meter", @@ -45,7 +34,6 @@ def test_flush_and_load_house() -> None: "Version": "100", } - ElectricMeterCacGt_Maker.dict_to_dc(unknown_electric_meter_cac_dict) ElectricMeterComponentGt_Maker.dict_to_dc(electric_meter_component_dict) SpaceheatNodeGt_Maker.dict_to_dc(meter_node_dict) assert ShNode.by_id["c9456f5b-5a39-4a48-bb91-742a9fdc461d"].alias == "a.m" diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 75841007..12bc3a15 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -1,91 +1,17 @@ """Tests component.attribute.class.gt type, version 000""" -import json +from gwproto.types import ComponentAttributeClassGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.errors import SchemaError -from gwproto.types import ComponentAttributeClassGt_Maker as Maker - - -def test_component_attribute_class_gt_generated() -> None: +def test_component_attribute_class_gt_load() -> None: d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "TypeName": "component.attribute.class.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load( + [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] + ) diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index b267619b..298e74d0 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -1,141 +1,22 @@ """Tests electric.meter.cac.gt type, version 000""" -import json +from gwproto.types import ElectricMeterCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import LocalCommInterface, MakeModel -from gwproto.errors import SchemaError -from gwproto.types import ElectricMeterCacGt_Maker as Maker - - -def test_electric_meter_cac_gt_generated() -> None: +def test_electric_meter_cac_load() -> None: d = { "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "MakeModelGtEnumSymbol": "d300635e", + # "MakeModelGtEnumSymbol": "d300635e", + "MakeModel": "SCHNEIDERELECTRIC__IEM3455", "DisplayName": "Schneider Electric Iem3455 Power Meter", - "TelemetryNameList": ["af39eec9"], + # "TelemetryNameList": ["af39eec9"], + "TelemetryNameList": ["PowerW"], "PollPeriodMs": 1000, - "InterfaceGtEnumSymbol": "a6a4ac9f", + # "InterfaceGtEnumSymbol": "a6a4ac9f", + "Interface": "RS485", "DefaultBaud": 9600, "TypeName": "electric.meter.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - display_name=gtuple.DisplayName, - telemetry_name_list=gtuple.TelemetryNameList, - poll_period_ms=gtuple.PollPeriodMs, - interface=gtuple.Interface, - default_baud=gtuple.DefaultBaud, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["PollPeriodMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["InterfaceGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "DefaultBaud" in d2: - del d2["DefaultBaud"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - d2 = dict(d, PollPeriodMs="1000.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, InterfaceGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).Interface == LocalCommInterface.default() - - d2 = dict(d, DefaultBaud="9600.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("ElectricMeterCac", d, ElectricMeterCacGt)]) diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 59c1a65f..bac4ff6a 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -1,159 +1,24 @@ """Tests multipurpose.sensor.cac.gt type, version 000""" -import json +from gwproto.types import MultipurposeSensorCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import MakeModel, Unit -from gwproto.errors import SchemaError -from gwproto.types import MultipurposeSensorCacGt_Maker as Maker - - -def test_multipurpose_sensor_cac_gt_generated() -> None: +def test_multipurpose_sensor_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "MakeModelGtEnumSymbol": "09185ae3", + # "MakeModelGtEnumSymbol": "09185ae3", + "MakeModel": "GRIDWORKS__MULTITEMP1", "PollPeriodMs": 880, "Exponent": -3, - "TempUnitGtEnumSymbol": "8e6dd6dd", - "TelemetryNameList": ["22641963"], + # "TempUnitGtEnumSymbol": "8e6dd6dd", + "TempUnit": "Celcius", + # "TelemetryNameList": ["22641963"], + "TelemetryNameList": ["WaterTempCTimes1000"], "MaxThermistors": 12, "DisplayName": "Simulated GridWorks high precision water temp sensor", "CommsMethod": "I2C", "TypeName": "multipurpose.sensor.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - poll_period_ms=gtuple.PollPeriodMs, - exponent=gtuple.Exponent, - temp_unit=gtuple.TempUnit, - telemetry_name_list=gtuple.TelemetryNameList, - max_thermistors=gtuple.MaxThermistors, - display_name=gtuple.DisplayName, - comms_method=gtuple.CommsMethod, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["PollPeriodMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Exponent"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TempUnitGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "MaxThermistors" in d2: - del d2["MaxThermistors"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "CommsMethod" in d2: - del d2["CommsMethod"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - d2 = dict(d, PollPeriodMs="880.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Exponent="-3.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() - - d2 = dict(d, MaxThermistors="12.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("MultipurposeSensorCacGt", d, MultipurposeSensorCacGt)]) diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index 43f165a5..2b5c3e47 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -1,109 +1,17 @@ """Tests pipe.flow.sensor.cac.gt type, version 000""" -import json +from gwproto.types import PipeFlowSensorCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import MakeModel -from gwproto.errors import SchemaError -from gwproto.types import PipeFlowSensorCacGt_Maker as Maker - - -def test_pipe_flow_sensor_cac_gt_generated() -> None: +def test_pipe_flow_sensor_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModelGtEnumSymbol": "d0b0e375", + # "MakeModelGtEnumSymbol": "d0b0e375", + "MakeModel": "ATLAS__EZFLO", "DisplayName": "EZFLO for a.tank.out", "CommsMethod": "I2C", "TypeName": "pipe.flow.sensor.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - display_name=gtuple.DisplayName, - comms_method=gtuple.CommsMethod, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "CommsMethod" in d2: - del d2["CommsMethod"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("PipeFlowSensorCacGt", d, PipeFlowSensorCacGt)]) diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index 8049a5a9..13aa3cd1 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -1,113 +1,17 @@ """Tests relay.cac.gt type, version 000""" -import json +from gwproto.types import RelayCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import MakeModel -from gwproto.errors import SchemaError -from gwproto.types import RelayCacGt_Maker as Maker - - -def test_relay_cac_gt_generated() -> None: +def test_relay_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "MakeModelGtEnumSymbol": "9cc57878", + # "MakeModelGtEnumSymbol": "9cc57878", + "MakeModel": "GRIDWORKS__SIMBOOL30AMPRELAY", "DisplayName": "Gridworks Simulated Boolean Actuator", "TypicalResponseTimeMs": 400, "TypeName": "relay.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - display_name=gtuple.DisplayName, - typical_response_time_ms=gtuple.TypicalResponseTimeMs, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TypicalResponseTimeMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - d2 = dict(d, TypicalResponseTimeMs="400.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("RelayCacGt", d, RelayCacGt)]) diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 142fb1b7..4eae9ce0 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -1,128 +1,18 @@ """Tests resistive.heater.cac.gt type, version 000""" -import json +from gwproto.types import ResistiveHeaterCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import MakeModel -from gwproto.errors import SchemaError -from gwproto.types import ResistiveHeaterCacGt_Maker as Maker - - -def test_resistive_heater_cac_gt_generated() -> None: +def test_resistive_heater_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModelGtEnumSymbol": "00000000", + # "MakeModelGtEnumSymbol": "00000000", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "DisplayName": "Fake Boost Element", "NameplateMaxPowerW": 4500, "RatedVoltageV": 240, "TypeName": "resistive.heater.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - display_name=gtuple.DisplayName, - nameplate_max_power_w=gtuple.NameplateMaxPowerW, - rated_voltage_v=gtuple.RatedVoltageV, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["NameplateMaxPowerW"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RatedVoltageV"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - d2 = dict(d, NameplateMaxPowerW="4500.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, RatedVoltageV="240.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, RatedVoltageV=0) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("ResistiveHeaterCacGt", d, ResistiveHeaterCacGt)]) diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index b55976fb..e2294ccc 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -1,151 +1,23 @@ """Tests simple.temp.sensor.cac.gt type, version 000""" -import json +from gwproto.types import SimpleTempSensorCacGt +from tests.cac_load_utils import CacCase, assert_cac_load -import pytest -from pydantic import ValidationError -from gwproto.enums import MakeModel, TelemetryName, Unit -from gwproto.errors import SchemaError -from gwproto.types import SimpleTempSensorCacGt_Maker as Maker - - -def test_simple_temp_sensor_cac_gt_generated() -> None: +def test_simple_temp_sensor_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "MakeModelGtEnumSymbol": "acd93fb3", + # "MakeModelGtEnumSymbol": "acd93fb3", + "MakeModel": "ADAFRUIT__642", "TypicalResponseTimeMs": 880, "Exponent": -3, - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + # "TempUnitGtEnumSymbol": "ec14bd47", + "TempUnit": "Celcius", + "TelemetryNameGtEnumSymbol": "WaterTempCTimes1000", + "TelemetryName": "", "DisplayName": "Simulated GridWorks high precision water temp sensor", "CommsMethod": "SassyMQ", "TypeName": "simple.temp.sensor.cac.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_attribute_class_id=gtuple.ComponentAttributeClassId, - make_model=gtuple.MakeModel, - typical_response_time_ms=gtuple.TypicalResponseTimeMs, - exponent=gtuple.Exponent, - temp_unit=gtuple.TempUnit, - telemetry_name=gtuple.TelemetryName, - display_name=gtuple.DisplayName, - comms_method=gtuple.CommsMethod, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MakeModelGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TypicalResponseTimeMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Exponent"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TempUnitGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "CommsMethod" in d2: - del d2["CommsMethod"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() - - d2 = dict(d, TypicalResponseTimeMs="880.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Exponent="-3.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() - - d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_cac_load([CacCase("SimpleTempSensorCacGt", d, SimpleTempSensorCacGt)]) From 54437cc2caba9e8f2ffdcd224ba879307a9cde7f Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 3 Sep 2024 17:56:55 -0400 Subject: [PATCH 093/168] WIP: enum symbolization experiments: model_serializer --- .github/workflows/tests.yml | 2 + src/gwproto/enums/symbolized.py | 51 +++++++++++++++++++ .../types/component_attribute_class_gt.py | 32 +++++++++++- .../test_component_attribute_class_gt.py | 3 +- 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b8635378..5ea428ed 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,6 +98,8 @@ jobs: - name: Run Nox run: | nox --python=${{ matrix.python }} + pwd + ls -a1 - name: Upload coverage data if: always() && matrix.session == 'tests' diff --git a/src/gwproto/enums/symbolized.py b/src/gwproto/enums/symbolized.py index f8490f44..9558bb1c 100644 --- a/src/gwproto/enums/symbolized.py +++ b/src/gwproto/enums/symbolized.py @@ -1,3 +1,6 @@ +import os +from typing import Type + from gwproto.enums.better_str_enum import BetterStrEnum @@ -9,3 +12,51 @@ def symbol_to_value(cls, symbol: str) -> str: @classmethod def value_to_symbol(cls, value: str) -> str: raise NotImplementedError + + +DEFAULT_SYMBOLIZED_TAG = "GtEnumSymbol" +SYMBOLIZE_ENV_VAR = "SYMBOLIZE_GRIDWORKS_ENUMS" + + +def symbolizing() -> bool: + return os.getenv(SYMBOLIZE_ENV_VAR, "1").lower() not in ("0", "false") + + +def default_enum_name(symbolized_name: str) -> str: + return symbolized_name[: -len(DEFAULT_SYMBOLIZED_TAG)] + + +def default_symbolized_name(enum_name: str) -> str: + return enum_name + DEFAULT_SYMBOLIZED_TAG + + +def symbolize( + d: dict, + *, + enum_class: Type[SymbolizedEnum], + enum_name: str = "", + symbolized_name: str = "", +) -> None: + if not enum_name: + enum_name = enum_class.__name__ + if not symbolized_name: + symbolized_name = default_symbolized_name(enum_name) + if enum_name in d: + d[symbolized_name] = enum_class.value_to_symbol(d[enum_name]) + del d[enum_name] + + +def desymbolize( + d: dict, + *, + symbolized_name: str, + enum_class: Type[SymbolizedEnum], + enum_name: str = "", +) -> None: + if not enum_name: + enum_name = default_enum_name(symbolized_name) + if symbolized_name in d: + if not enum_name: + enum_name = symbolized_name[: -len("GtEnumSymbol")] + d[enum_name] = enum_class.symbol_to_value(d[symbolized_name]) + del d[symbolized_name] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index dd96a865..7e50db73 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -1,10 +1,11 @@ """Type component.attribute.class.gt, version 000""" -from typing import Literal, Optional +from typing import Any, Dict, Literal, Optional -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict, model_serializer, model_validator from gwproto.enums import MakeModel +from gwproto.enums.symbolized import desymbolize, symbolize, symbolizing from gwproto.utils import UUID4Str @@ -14,3 +15,30 @@ class ComponentAttributeClassGt(BaseModel): MakeModel: MakeModel TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" + + model_config = ConfigDict( + extra="allow", + # comment these out to cut down on experimental headaches + # alias_generator=camel_to_snake, # noqa: ERA001 + # populate_by_name=True # noqa: ERA001 + use_enum_values=True, + ) + + @model_validator(mode="before") + @classmethod + def desymbolize(cls, data: Any) -> Any: # noqa: ANN401 + if symbolizing(): + desymbolize( + data, + symbolized_name="MakeModelGtEnumSymbol", + enum_class=MakeModel, + ) + return data + + @model_serializer(when_used="json") + def symbolize(self) -> Dict[str, Any]: + # DRAWBACK: we lose parameters passed to model_dump_json + d = self.model_dump() + if symbolizing(): + symbolize(d, enum_class=MakeModel) + return d diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 12bc3a15..7f1b7814 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -8,7 +8,8 @@ def test_component_attribute_class_gt_load() -> None: d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "MakeModelGtEnumSymbol": "00000000", + # "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "TypeName": "component.attribute.class.gt", "Version": "000", } From 19c4360e1f1124212e27b695f29ad514570eb8f1 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 3 Sep 2024 18:33:33 -0400 Subject: [PATCH 094/168] Improved model_serializer --- .github/workflows/tests.yml | 8 ++++---- src/gwproto/types/component_attribute_class_gt.py | 7 +++---- tests/types/test_component_attribute_class_gt.py | 7 +++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5ea428ed..2618c9d6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,10 +16,10 @@ jobs: # - { python: "3.12", os: "ubuntu-latest", session: "mypy" } - { python: "3.11", os: "ubuntu-latest", session: "tests" } - { python: "3.12", os: "ubuntu-latest", session: "tests" } - - { python: "3.12", os: "windows-latest", session: "tests" } - - { python: "3.12", os: "macos-latest", session: "tests" } - - { python: "3.12", os: "ubuntu-latest", session: "xdoctest" } - - { python: "3.12", os: "ubuntu-latest", session: "docs-build" } + # - { python: "3.12", os: "windows-latest", session: "tests" } + # - { python: "3.12", os: "macos-latest", session: "tests" } + # - { python: "3.12", os: "ubuntu-latest", session: "xdoctest" } + # - { python: "3.12", os: "ubuntu-latest", session: "docs-build" } env: NOXSESSION: ${{ matrix.session }} FORCE_COLOR: "1" diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 7e50db73..3a496949 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -35,10 +35,9 @@ def desymbolize(cls, data: Any) -> Any: # noqa: ANN401 ) return data - @model_serializer(when_used="json") - def symbolize(self) -> Dict[str, Any]: - # DRAWBACK: we lose parameters passed to model_dump_json - d = self.model_dump() + @model_serializer(when_used="json", mode="wrap") + def symbolize(self, handler, info) -> Dict[str, Any]: # noqa: ANN001 + d = handler(self, info).__dict__ if symbolizing(): symbolize(d, enum_class=MakeModel) return d diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 7f1b7814..51f2f70e 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -1,15 +1,18 @@ """Tests component.attribute.class.gt type, version 000""" +from typing import Any + +from gwproto.enums.symbolized import SYMBOLIZE_ENV_VAR from gwproto.types import ComponentAttributeClassGt from tests.cac_load_utils import CacCase, assert_cac_load -def test_component_attribute_class_gt_load() -> None: +def test_component_attribute_class_gt_load(monkeypatch: Any) -> None: # noqa: ANN401 + monkeypatch.setenv(SYMBOLIZE_ENV_VAR, "1") d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", "MakeModelGtEnumSymbol": "00000000", - # "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "TypeName": "component.attribute.class.gt", "Version": "000", } From 39f349b6c7aee2b6dfd0aedf73cf8fc2464334af Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 3 Sep 2024 18:43:58 -0400 Subject: [PATCH 095/168] ... better ... but a faulty model_serializer doesn't crash the tests ... so something is up ... --- src/gwproto/types/component_attribute_class_gt.py | 2 +- tests/cac_load_utils.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 3a496949..fb3c4519 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -37,7 +37,7 @@ def desymbolize(cls, data: Any) -> Any: # noqa: ANN401 @model_serializer(when_used="json", mode="wrap") def symbolize(self, handler, info) -> Dict[str, Any]: # noqa: ANN001 - d = handler(self, info).__dict__ + d = handler(self, info) if symbolizing(): symbolize(d, enum_class=MakeModel) return d diff --git a/tests/cac_load_utils.py b/tests/cac_load_utils.py index 57ddc227..24faa64e 100644 --- a/tests/cac_load_utils.py +++ b/tests/cac_load_utils.py @@ -1,3 +1,4 @@ +import json from dataclasses import dataclass, field from typing import Optional, Type @@ -12,7 +13,7 @@ class CacCase: tag: str src_cac: ComponentAttributeClassGt | dict - exp_cac_type: Optional[Type] = ComponentAttributeClassGt + exp_cac_type: Optional[Type[ComponentAttributeClassGt]] = ComponentAttributeClassGt exp_cac: Optional[ComponentAttributeClassGt | dict] = None exp_exceptions: list[Type[Exception]] = field(default_factory=list) @@ -62,10 +63,11 @@ def _decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: if decoder is None: decoder = default_cac_decoder cac_dict = ( - case.src_cac.model_dump() + json.loads(case.src_cac.model_dump_json()) if isinstance(case.src_cac, ComponentAttributeClassGt) else case.src_cac ) + case.exp_cac_type.model_validate(cac_dict) cac_id = cac_dict["ComponentAttributeClassId"] try: loaded_cac = load_cacs( From d24c196711fa4b11f6f73eaf57417c435b5db962 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 4 Sep 2024 11:19:58 -0400 Subject: [PATCH 096/168] assert_cac_load now forces model_dump_json(); assert_symbolized_and_unsymbolized_encode() explicitly tests symbolized and unsymbolized encoding --- pyproject.toml | 7 +++- .../types/component_attribute_class_gt.py | 14 +++++-- tests/cac_load_utils.py | 42 ++++++++++++++----- .../test_component_attribute_class_gt.py | 34 ++++++++++++++- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b3988b07..f2418fd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,14 +121,18 @@ show-fixes = true # select = ["B","C90","E4", "E7", "E9","F","I","N","PL", "TRY", "W",] select = ["ALL"] ignore = [ + "ANN101", # Deprecated + "ANN102", # Deprecated + "ANN401", # When we use 'Any' we mean it. "B027", "B904", # Suppress for now; lots of them generated by code generation. "COM812", "CPY", "D", "DOC", - "E272", # Formatter "E241", # Formatter + "E252", # Formatter + "E272", # Formatter "E302", "E501", "EM", @@ -146,6 +150,7 @@ ignore = [ "RUF100", # ruff and IDE often disagree about whether a 'noqa' is in use. "W191", "W291", # Formatter + "W293", # Formatter ] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index fb3c4519..b3528774 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -1,8 +1,14 @@ """Type component.attribute.class.gt, version 000""" -from typing import Any, Dict, Literal, Optional +from typing import Any, Callable, Dict, Literal, Optional -from pydantic import BaseModel, ConfigDict, model_serializer, model_validator +from pydantic import ( + BaseModel, + ConfigDict, + SerializationInfo, + model_serializer, + model_validator, +) from gwproto.enums import MakeModel from gwproto.enums.symbolized import desymbolize, symbolize, symbolizing @@ -26,7 +32,7 @@ class ComponentAttributeClassGt(BaseModel): @model_validator(mode="before") @classmethod - def desymbolize(cls, data: Any) -> Any: # noqa: ANN401 + def desymbolize(cls, data: Any) -> Any: if symbolizing(): desymbolize( data, @@ -36,7 +42,7 @@ def desymbolize(cls, data: Any) -> Any: # noqa: ANN401 return data @model_serializer(when_used="json", mode="wrap") - def symbolize(self, handler, info) -> Dict[str, Any]: # noqa: ANN001 + def symbolize(self, handler: Callable, info: SerializationInfo) -> Dict[str, Any]: # noqa: ANN001 d = handler(self, info) if symbolizing(): symbolize(d, enum_class=MakeModel) diff --git a/tests/cac_load_utils.py b/tests/cac_load_utils.py index 24faa64e..0dd9bd6c 100644 --- a/tests/cac_load_utils.py +++ b/tests/cac_load_utils.py @@ -1,11 +1,14 @@ import json from dataclasses import dataclass, field -from typing import Optional, Type +from typing import Any, Optional, Type + +from pydantic import BaseModel from gwproto import CacDecoder, default_cac_decoder from gwproto.data_classes.hardware_layout import ( load_cacs, ) +from gwproto.enums.symbolized import SYMBOLIZE_ENV_VAR from gwproto.types import ComponentAttributeClassGt @@ -13,7 +16,7 @@ class CacCase: tag: str src_cac: ComponentAttributeClassGt | dict - exp_cac_type: Optional[Type[ComponentAttributeClassGt]] = ComponentAttributeClassGt + exp_cac_type: Type[ComponentAttributeClassGt] = ComponentAttributeClassGt exp_cac: Optional[ComponentAttributeClassGt | dict] = None exp_exceptions: list[Type[Exception]] = field(default_factory=list) @@ -59,15 +62,16 @@ class CacLoadResult: exception: Exception | None -def _decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: +def _encode_decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: if decoder is None: decoder = default_cac_decoder - cac_dict = ( - json.loads(case.src_cac.model_dump_json()) - if isinstance(case.src_cac, ComponentAttributeClassGt) - else case.src_cac - ) - case.exp_cac_type.model_validate(cac_dict) + # Force call to model_dump_json() by encoding dict as model to ensure + # serialization is tested. + if isinstance(case.src_cac, ComponentAttributeClassGt): + cac = case.src_cac + else: + cac = case.exp_cac_type.model_validate(case.src_cac) + cac_dict = json.loads(cac.model_dump_json()) cac_id = cac_dict["ComponentAttributeClassId"] try: loaded_cac = load_cacs( @@ -89,7 +93,7 @@ def _decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: def assert_cac_load(cases: list[CacCase], decoder: Optional[CacDecoder] = None) -> None: errors: list[CacCaseError] = [] for case_idx, case in enumerate(cases): - load_result = _decode_cac(case, decoder) + load_result = _encode_decode_cac(case, decoder) if not load_result.ok: errors.append(CacLoadError(case_idx, case, load_result.exception)) elif not case.exp_exceptions: @@ -115,3 +119,21 @@ def assert_cac_load(cases: list[CacCase], decoder: Optional[CacDecoder] = None) if first_exception is not None: raise ValueError(err_str) from first_exception raise ValueError(err_str) + + +def assert_symbolized_and_unsymbolized_encode( + monkeypatch: Any, + m: BaseModel, + exp_unsymbolized: dict, + exp_symbolized: dict, +) -> None: + for symbolization, exp_dict in [ + ("0", exp_unsymbolized), + ("1", exp_symbolized), + ]: + with monkeypatch.context() as monkey: + monkey.setenv(SYMBOLIZE_ENV_VAR, symbolization) + d = json.loads(m.model_dump_json()) + assert d == exp_dict + m2 = m.__class__.model_validate(d) + assert m == m2 diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 51f2f70e..860186c6 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -2,12 +2,17 @@ from typing import Any +from gwproto.enums import MakeModel from gwproto.enums.symbolized import SYMBOLIZE_ENV_VAR from gwproto.types import ComponentAttributeClassGt -from tests.cac_load_utils import CacCase, assert_cac_load +from tests.cac_load_utils import ( + CacCase, + assert_cac_load, + assert_symbolized_and_unsymbolized_encode, +) -def test_component_attribute_class_gt_load(monkeypatch: Any) -> None: # noqa: ANN401 +def test_component_attribute_class_gt_load(monkeypatch: Any) -> None: monkeypatch.setenv(SYMBOLIZE_ENV_VAR, "1") d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", @@ -19,3 +24,28 @@ def test_component_attribute_class_gt_load(monkeypatch: Any) -> None: # noqa: A assert_cac_load( [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] ) + + +def test_encode_decode(monkeypatch: Any) -> None: + assert_symbolized_and_unsymbolized_encode( + monkeypatch=monkeypatch, + m=ComponentAttributeClassGt( + ComponentAttributeClassId="29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", + DisplayName="Sample CAC", + MakeModel=MakeModel.UNKNOWNMAKE__UNKNOWNMODEL, + ), + exp_unsymbolized={ + "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", + "DisplayName": "Sample CAC", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt", + "Version": "000", + }, + exp_symbolized={ + "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", + "DisplayName": "Sample CAC", + "MakeModelGtEnumSymbol": "00000000", + "TypeName": "component.attribute.class.gt", + "Version": "000", + }, + ) From 023323be3f9778735066d1cd5bf48da0ea8dd147 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 4 Sep 2024 16:15:39 -0400 Subject: [PATCH 097/168] CI: Fix coverage upload/download --- .github/workflows/tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b8635378..94749ec4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -101,10 +101,12 @@ jobs: - name: Upload coverage data if: always() && matrix.session == 'tests' - uses: "actions/upload-artifact@v3" + uses: "actions/upload-artifact@v4.4.0" with: name: coverage-data path: ".coverage.*" + if-no-files-found: error + include-hidden-files: true - name: Upload documentation if: matrix.session == 'docs-build' @@ -142,7 +144,7 @@ jobs: nox --version - name: Download coverage data - uses: actions/download-artifact@v3 + uses: "actions/download-artifact@v4.1.8" with: name: coverage-data From b2622850a9ca061f52dba26793fea95b62255322 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 4 Sep 2024 18:09:38 -0400 Subject: [PATCH 098/168] CI: Fix coverage upload (#340) Update the versions of of upload-artifact download-artifact actions. Coverage now only runs on one version of python; previously we would just overwrite the coverage data with whichever test ran last; uploading an artifact with the same name twice is no longer allowed. --- .github/workflows/tests.yml | 19 +++++++++++++++---- noxfile.py | 20 ++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 94749ec4..9f2ff171 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: - { python: "3.12", os: "ubuntu-latest", session: "pre-commit" } # - { python: "3.12", os: "ubuntu-latest", session: "mypy" } - { python: "3.11", os: "ubuntu-latest", session: "tests" } - - { python: "3.12", os: "ubuntu-latest", session: "tests" } + - { python: "3.12", os: "ubuntu-latest", session: "tests", coverage: true} - { python: "3.12", os: "windows-latest", session: "tests" } - { python: "3.12", os: "macos-latest", session: "tests" } - { python: "3.12", os: "ubuntu-latest", session: "xdoctest" } @@ -95,12 +95,23 @@ jobs: restore-keys: | ${{ steps.pre-commit-cache.outputs.result }}- - - name: Run Nox + - name: Run Tests with coverage + if: matrix.session == 'tests' && matrix.coverage == true + run: | + nox --python=${{ matrix.python }} + + - name: Run Tests without coverage + if: matrix.session == 'tests' && matrix.coverage != true + run: | + nox --python=${{ matrix.python }} -- --no-coverage + + - name: Run non-test session + if: matrix.session != 'tests' run: | nox --python=${{ matrix.python }} - name: Upload coverage data - if: always() && matrix.session == 'tests' + if: always() && matrix.session == 'tests' && matrix.coverage == true uses: "actions/upload-artifact@v4.4.0" with: name: coverage-data @@ -110,7 +121,7 @@ jobs: - name: Upload documentation if: matrix.session == 'docs-build' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.0 with: name: docs path: docs/_build diff --git a/noxfile.py b/noxfile.py index 675327b0..abb12d22 100644 --- a/noxfile.py +++ b/noxfile.py @@ -149,12 +149,20 @@ def mypy(session: Session) -> None: def tests(session: Session) -> None: """Run the test suite.""" session.install(".") - session.install("coverage[toml]", "pytest", "pygments") - try: - session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs) - finally: - if session.interactive: - session.notify("coverage", posargs=[]) + session.install("pytest", "pygments") + if not session.posargs or ( + session.posargs and session.posargs[0] != "--no-coverage" + ): + session.install("coverage[toml]") + try: + session.run( + "coverage", "run", "--parallel", "-m", "pytest", *session.posargs + ) + finally: + if session.interactive: + session.notify("coverage", posargs=[]) + else: + session.run("pytest", *session.posargs[1:]) @session(python=python_versions[0]) From 9ba8076a5deddc630add5c7c09133fd46ff557e0 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 5 Sep 2024 12:24:26 -0400 Subject: [PATCH 099/168] Revert "Merge branch 'as/cacs-enums-model-serializer' into dev" This reverts commit 9b95313072ca76eaa677b88dbfa7fa5d0308b1e1, reversing changes made to b2622850a9ca061f52dba26793fea95b62255322. --- .github/workflows/tests.yml | 2 - pyproject.toml | 9 - src/gwproto/__init__.py | 4 +- src/gwproto/data_classes/hardware_layout.py | 91 +++-- src/gwproto/decoders.py | 2 +- src/gwproto/default_decoders.py | 56 +-- src/gwproto/enums/actor_class.py | 11 +- src/gwproto/enums/better_str_enum.py | 17 +- src/gwproto/enums/local_comm_interface.py | 11 +- src/gwproto/enums/make_model.py | 11 +- src/gwproto/enums/role.py | 11 +- src/gwproto/enums/symbolized.py | 62 ---- src/gwproto/enums/telemetry_name.py | 11 +- src/gwproto/enums/unit.py | 11 +- src/gwproto/types/__init__.py | 25 +- src/gwproto/types/cacs.py | 27 -- .../types/component_attribute_class_gt.py | 275 ++++++++++++-- src/gwproto/types/electric_meter_cac_gt.py | 337 ++++++++++++++++- .../types/fibaro_smart_implant_cac_gt.py | 67 +++- src/gwproto/types/hubitat_cac_gt.py | 23 ++ src/gwproto/types/hubitat_poller_cac_gt.py | 29 ++ src/gwproto/types/hubitat_tank_cac_gt.py | 65 ++++ .../types/multipurpose_sensor_cac_gt.py | 343 +++++++++++++++++- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 263 +++++++++++++- src/gwproto/types/relay_cac_gt.py | 263 +++++++++++++- src/gwproto/types/resistive_heater_cac_gt.py | 302 ++++++++++++++- src/gwproto/types/rest_poller_cac_gt.py | 67 +++- .../types/simple_temp_sensor_cac_gt.py | 320 +++++++++++++++- src/gwproto/types/web_server_cac_gt.py | 23 ++ src/gwproto/utils.py | 18 - tests/cac_load_utils.py | 139 ------- tests/data_classes/test_electric_meter_cac.py | 52 +-- .../test_electric_meter_component.py | 5 +- tests/test_misc/test_flush_and_load_house.py | 14 +- .../test_component_attribute_class_gt.py | 120 ++++-- tests/types/test_electric_meter_cac_gt.py | 139 ++++++- .../types/test_multipurpose_sensor_cac_gt.py | 155 +++++++- tests/types/test_pipe_flow_sensor_cac_gt.py | 104 +++++- tests/types/test_relay_cac_gt.py | 108 +++++- tests/types/test_resistive_heater_cac_gt.py | 122 ++++++- tests/types/test_simple_temp_sensor_cac_gt.py | 148 +++++++- 41 files changed, 3306 insertions(+), 556 deletions(-) delete mode 100644 src/gwproto/enums/symbolized.py delete mode 100644 src/gwproto/types/cacs.py delete mode 100644 tests/cac_load_utils.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 776bee04..9f2ff171 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -109,8 +109,6 @@ jobs: if: matrix.session != 'tests' run: | nox --python=${{ matrix.python }} - pwd - ls -a1 - name: Upload coverage data if: always() && matrix.session == 'tests' && matrix.coverage == true diff --git a/pyproject.toml b/pyproject.toml index f2418fd6..db179e20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,18 +121,12 @@ show-fixes = true # select = ["B","C90","E4", "E7", "E9","F","I","N","PL", "TRY", "W",] select = ["ALL"] ignore = [ - "ANN101", # Deprecated - "ANN102", # Deprecated - "ANN401", # When we use 'Any' we mean it. "B027", "B904", # Suppress for now; lots of them generated by code generation. "COM812", "CPY", "D", "DOC", - "E241", # Formatter - "E252", # Formatter - "E272", # Formatter "E302", "E501", "EM", @@ -149,8 +143,6 @@ ignore = [ "TRY003", # Many of our ValueErrors contain messages that are not plausibly resuable. "RUF100", # ruff and IDE often disagree about whether a 'noqa' is in use. "W191", - "W291", # Formatter - "W293", # Formatter ] @@ -159,7 +151,6 @@ ignore = [ "tests/**/*.py" = [ "ARG001", # Unused function args -> fixtures nevertheless are functionally relevant... "C901", # Complexity - "ERA001", # We need to be able comment out code in tests, at least during refactoring. "FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize() "G004", # Ok to use f-strings in logging in test code "PLR0912", # Complexity diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index 3aaa1fef..b2e11b14 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -56,7 +56,7 @@ "default_cac_decoder", "default_component_decoder", "get_pydantic_literal_type_name", - "messages", # noqa: F822 - "property_format", # noqa: F822 + "messages", + "property_format", "pydantic_named_types", ] diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 8869d860..81e40a38 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -17,6 +17,7 @@ from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.component import Component +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) @@ -32,10 +33,15 @@ ) from gwproto.enums import ActorClass, Role, TelemetryName from gwproto.types import ( - ComponentAttributeClassGt, + ElectricMeterCacGt_Maker, + MultipurposeSensorCacGt_Maker, + PipeFlowSensorCacGt_Maker, PipeFlowSensorComponentGt_Maker, + RelayCacGt_Maker, RelayComponentGt_Maker, + ResistiveHeaterCacGt_Maker, ResistiveHeaterComponentGt_Maker, + SimpleTempSensorCacGt_Maker, SimpleTempSensorComponentGt_Maker, SpaceheatNodeGt_Maker, ) @@ -60,42 +66,54 @@ class LoadError: exception: Exception -def load_cacs( +def load_cacs( # noqa: C901 layout: dict[str, Any], - *, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, -) -> dict[str, ComponentAttributeClassGt]: +) -> dict[str, Any]: if errors is None: errors = [] - if cac_decoder is None: - cac_decoder = default_cac_decoder cacs = {} - for type_name in [ - "RelayCacs", - "ResistiveHeaterCacs", - "ElectricMeterCacs", - "PipeFlowSensorCacs", - "MultipurposeSensorCacs", - "SimpleTempSensorCacs", - "OtherCacs", + for type_name, maker_class in [ + ("RelayCacs", RelayCacGt_Maker), + ("ResistiveHeaterCacs", ResistiveHeaterCacGt_Maker), + ("ElectricMeterCacs", ElectricMeterCacGt_Maker), + ("PipeFlowSensorCacs", PipeFlowSensorCacGt_Maker), + ("MultipurposeSensorCacs", MultipurposeSensorCacGt_Maker), + ("SimpleTempSensorCacs", SimpleTempSensorCacGt_Maker), ]: - for cac_dict in layout.get(type_name, ()): + for d in layout.get(type_name, []): try: - cac = cac_decoder.decode(cac_dict) - cacs[cac.ComponentAttributeClassId] = cac + cacs[d["ComponentAttributeClassId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] + d + ) except Exception as e: # noqa: PERF203 if raise_errors: raise - errors.append(LoadError(type_name, cac_dict, e)) + errors.append(LoadError(type_name, d, e)) + if cac_decoder is None: + cac_decoder = default_cac_decoder + for d in layout.get("OtherCacs", []): + cac_type = d.get("TypeName", "") + try: + if cac_type and cac_type in cac_decoder: + cac = cac_decoder.decode_to_data_class(d) + else: + cac = ComponentAttributeClass( + component_attribute_class_id=d["ComponentAttributeClassId"] + ) + cacs[d["ComponentAttributeClassId"]] = cac + except Exception as e: + if raise_errors: + raise + errors.append(LoadError("OtherCacs", d, e)) return cacs def load_components( # noqa: C901 layout: dict[Any, Any], - *, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, component_decoder: Optional[ComponentDecoder] = None, ) -> dict[Any, Any]: @@ -138,8 +156,7 @@ def load_components( # noqa: C901 def load_nodes( layout: dict[Any, Any], - *, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, included_node_names: Optional[set[str]] = None, ) -> dict[Any, Any]: @@ -161,8 +178,7 @@ def load_nodes( def resolve_links( nodes: dict[str, ShNode], components: dict[str, Component], - *, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, ) -> None: if errors is None: @@ -190,7 +206,7 @@ def resolve_links( class HardwareLayout: layout: dict[Any, Any] - cacs: dict[str, ComponentAttributeClassGt] + cacs: dict[str, ComponentAttributeClass] components: dict[str, Component] components_by_type: dict[Type, list[Component]] nodes: dict[str, ShNode] @@ -199,22 +215,19 @@ class HardwareLayout: def __init__( self, layout: dict[Any, Any], - *, - cacs: Optional[dict[str, ComponentAttributeClassGt]] = None, + cacs: Optional[dict[str, ComponentAttributeClass]] = None, components: Optional[dict[str, Component]] = None, nodes: Optional[dict[str, ShNode]] = None, ) -> None: self.layout = copy.deepcopy(layout) if cacs is None: - self.cacs = {} - else: - self.cacs = dict(cacs) + cacs = ComponentAttributeClass.by_id + self.cacs = dict(cacs) if components is None: - self.components = {} - else: - self.components = dict(components) + components = Component.by_id + self.components = dict(components) self.components_by_type = defaultdict(list) - for component in self.components.values(): + for component in components.values(): self.components_by_type[type(component)].append(component) if nodes is None: nodes = ShNode.by_id @@ -235,7 +248,6 @@ def clear_property_cache(self) -> None: def load( # noqa: PLR0913, PLR0917, RUF100 cls, layout_path: Path | str, - *, included_node_names: Optional[set[str]] = None, raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, @@ -257,9 +269,8 @@ def load( # noqa: PLR0913, PLR0917, RUF100 def load_dict( # noqa: PLR0913, PLR0917, RUF100 cls, layout: dict[Any, Any], - *, included_node_names: Optional[set[str]] = None, - raise_errors: bool = True, + raise_errors: bool = True, # noqa: FBT001, FBT002 errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, component_decoder: Optional[ComponentDecoder] = None, @@ -300,7 +311,7 @@ def node(self, alias: str, default: Any = None) -> ShNode: # noqa: ANN401 def component(self, node_alias: str) -> Optional[Component]: return self.component_from_node(self.node(node_alias, None)) - def cac(self, node_alias: str) -> Optional[ComponentAttributeClassGt]: + def cac(self, node_alias: str) -> Optional[ComponentAttributeClass]: return self.cac_from_component(self.component(node_alias)) def get_component_as_type(self, component_id: str, type_: Type[T]) -> Optional[T]: @@ -336,7 +347,7 @@ def component_from_node(self, node: Optional[ShNode]) -> Optional[Component]: def cac_from_component( self, component: Optional[Component] - ) -> Optional[ComponentAttributeClassGt]: + ) -> Optional[ComponentAttributeClass]: return ( self.cacs.get( component.component_attribute_class_id if component is not None else "", diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index d3005aae..94c8b4b3 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -296,7 +296,7 @@ def pydantic_named_types( # noqa: C901 if excluded_type_names is None: excluded_type_names = DEFAULT_EXCLUDED_TYPE_NAMES if isinstance(module_names, str): - module_names = [module_names] if module_names else [] + module_names = [module_names] if unimported := [ module_name for module_name in module_names if module_name not in sys.modules ]: diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index 8f79d550..23a8f8db 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -4,18 +4,21 @@ import typing from typing import Any, Type, TypeVar -from pydantic import ValidationError - -import gwproto.types.cacs +import gwproto.types.fibaro_smart_implant_cac_gt # noqa: F401 import gwproto.types.fibaro_smart_implant_component_gt # noqa: F401 +import gwproto.types.hubitat_cac_gt # noqa: F401 import gwproto.types.hubitat_component_gt # noqa: F401 +import gwproto.types.hubitat_poller_cac_gt # noqa: F401 import gwproto.types.hubitat_poller_component_gt # noqa: F401 +import gwproto.types.hubitat_tank_cac_gt # noqa: F401 import gwproto.types.hubitat_tank_component_gt # noqa: F401 +import gwproto.types.rest_poller_cac_gt # noqa: F401 import gwproto.types.rest_poller_component_gt # noqa: F401 +import gwproto.types.web_server_cac_gt # noqa: F401 import gwproto.types.web_server_component_gt # noqa: F401 from gwproto.data_classes.component import Component +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.decoders import PydanticTypeNameDecoder -from gwproto.types import ComponentAttributeClassGt T = TypeVar("T") @@ -23,9 +26,8 @@ def decode_to_data_class( decoded_gt: typing.Any, return_type: Type[T], - *, - allow_missing_func: bool = True, - allow_non_instance: bool = False, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 + allow_non_instance: bool = False, # noqa: FBT001, FBT002 ) -> T: if hasattr(decoded_gt, "to_data_class"): data_class = decoded_gt.to_data_class() @@ -50,24 +52,16 @@ def __init__(self, model_name: str, **kwargs: Any) -> None: kwargs["type_name_regex"] = CacDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) - def decode( - self, d: dict, *, allow_missing: bool = True - ) -> ComponentAttributeClassGt: - try: - decoded = self.loader.model_validate({self.payload_field_name: d}).Payload - if not isinstance(decoded, ComponentAttributeClassGt): - raise TypeError( - f"ERROR. CacDecoder decoded type {type(decoded)}, " - "not ComponentAttributeClassGt" - ) - except ValidationError as e: - if allow_missing and any( - error.get("type") == "union_tag_invalid" for error in e.errors() - ): - decoded = ComponentAttributeClassGt(**d) - else: - raise - return decoded + def decode_to_data_class( + self, + data: dict, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 + ) -> ComponentAttributeClass: + return decode_to_data_class( + decoded_gt=self.decode_obj(data), + return_type=ComponentAttributeClass, + allow_missing_func=allow_missing_func, + ) class ComponentDecoder(PydanticTypeNameDecoder): @@ -81,8 +75,7 @@ def __init__(self, model_name: str, **kwargs: Any) -> None: def decode_to_data_class( self, data: dict, - *, - allow_missing_func: bool = True, + allow_missing_func: bool = True, # noqa: FBT001, FBT002 ) -> Component: return decode_to_data_class( decoded_gt=self.decode_obj(data), @@ -93,7 +86,14 @@ def decode_to_data_class( default_cac_decoder = CacDecoder( model_name="DefaultCacDecoder", - modules=[gwproto.types.cacs], + module_names=[ + "gwproto.types.fibaro_smart_implant_cac_gt", + "gwproto.types.hubitat_cac_gt", + "gwproto.types.hubitat_poller_cac_gt", + "gwproto.types.hubitat_tank_cac_gt", + "gwproto.types.rest_poller_cac_gt", + "gwproto.types.web_server_cac_gt", + ], ) default_component_decoder = ComponentDecoder( diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 2cedbe62..0dff6dd9 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -2,10 +2,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class ActorClass(SymbolizedEnum): +class ActorClass(StrEnum): """ Determines the code running Spaceheat Nodes supervised by Spaceheat SCADA software @@ -82,6 +82,13 @@ def default(cls) -> "ActorClass": """ return cls.NoActor + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/better_str_enum.py b/src/gwproto/enums/better_str_enum.py index 64e1064e..364a3c98 100644 --- a/src/gwproto/enums/better_str_enum.py +++ b/src/gwproto/enums/better_str_enum.py @@ -1,5 +1,5 @@ from enum import StrEnum -from typing import Any, Optional, Self +from typing import Any class BetterStrEnum(StrEnum): @@ -11,18 +11,3 @@ def _generate_next_value_( last_values: list[Any], # noqa: ARG004 ) -> str: return name - - @classmethod - def values(cls) -> list[str]: - return [str(elt) for elt in cls] - - @classmethod - def default(cls) -> Optional[Self]: - return None - - @classmethod - def _missing_(cls, value: str) -> Self: - default = cls.default() - if default is None: - raise ValueError(f"'{value}' is not valid {cls.__name__}") - return default diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 5f42ce73..950b9d99 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class LocalCommInterface(SymbolizedEnum): +class LocalCommInterface(StrEnum): """ Categorization of in-house comm mechanisms for SCADA @@ -44,6 +44,13 @@ def default(cls) -> "LocalCommInterface": """ return cls.UNKNOWN + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa: ALL + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index ecaf7401..dbb186a1 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class MakeModel(SymbolizedEnum): +class MakeModel(StrEnum): """ Determines Make/Model of device associated to a Spaceheat Node supervised by SCADA @@ -89,6 +89,13 @@ def default(cls) -> "MakeModel": """ return cls.UNKNOWNMAKE__UNKNOWNMODEL + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa: ALL + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index d0644632..31f9f811 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class Role(SymbolizedEnum): +class Role(StrEnum): """ Categorizes SpaceheatNodes by their function within the heating system @@ -77,6 +77,13 @@ def default(cls) -> "Role": """ return cls.Unknown + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa: ALL + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/symbolized.py b/src/gwproto/enums/symbolized.py deleted file mode 100644 index 9558bb1c..00000000 --- a/src/gwproto/enums/symbolized.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -from typing import Type - -from gwproto.enums.better_str_enum import BetterStrEnum - - -class SymbolizedEnum(BetterStrEnum): - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - raise NotImplementedError - - @classmethod - def value_to_symbol(cls, value: str) -> str: - raise NotImplementedError - - -DEFAULT_SYMBOLIZED_TAG = "GtEnumSymbol" -SYMBOLIZE_ENV_VAR = "SYMBOLIZE_GRIDWORKS_ENUMS" - - -def symbolizing() -> bool: - return os.getenv(SYMBOLIZE_ENV_VAR, "1").lower() not in ("0", "false") - - -def default_enum_name(symbolized_name: str) -> str: - return symbolized_name[: -len(DEFAULT_SYMBOLIZED_TAG)] - - -def default_symbolized_name(enum_name: str) -> str: - return enum_name + DEFAULT_SYMBOLIZED_TAG - - -def symbolize( - d: dict, - *, - enum_class: Type[SymbolizedEnum], - enum_name: str = "", - symbolized_name: str = "", -) -> None: - if not enum_name: - enum_name = enum_class.__name__ - if not symbolized_name: - symbolized_name = default_symbolized_name(enum_name) - if enum_name in d: - d[symbolized_name] = enum_class.value_to_symbol(d[enum_name]) - del d[enum_name] - - -def desymbolize( - d: dict, - *, - symbolized_name: str, - enum_class: Type[SymbolizedEnum], - enum_name: str = "", -) -> None: - if not enum_name: - enum_name = default_enum_name(symbolized_name) - if symbolized_name in d: - if not enum_name: - enum_name = symbolized_name[: -len("GtEnumSymbol")] - d[enum_name] = enum_class.symbol_to_value(d[symbolized_name]) - del d[symbolized_name] diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index fd395993..0c38dcff 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class TelemetryName(SymbolizedEnum): +class TelemetryName(StrEnum): """ Specifies the name of sensed data reported by a Spaceheat SCADA @@ -66,6 +66,13 @@ def default(cls) -> "TelemetryName": """ return cls.Unknown + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa: ALL + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index b1e365bb..7dc04a1e 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -1,10 +1,10 @@ from enum import auto from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gwproto.enums.better_str_enum import BetterStrEnum as StrEnum -class Unit(SymbolizedEnum): +class Unit(StrEnum): """ Specifies the physical unit of sensed data reported by SCADA @@ -48,6 +48,13 @@ def default(cls) -> "Unit": """ return cls.Unknown + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] # noqa: ALL + @classmethod def version(cls, value: str) -> str: """ diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index d370029b..4bdfa232 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -2,6 +2,7 @@ from gwproto.types.component_attribute_class_gt import ( ComponentAttributeClassGt, + ComponentAttributeClassGt_Maker, ) from gwproto.types.component_gt import ComponentGt, ComponentGt_Maker from gwproto.types.data_channel import DataChannel, DataChannel_Maker @@ -12,9 +13,11 @@ ) from gwproto.types.electric_meter_cac_gt import ( ElectricMeterCacGt, + ElectricMeterCacGt_Maker, ) from gwproto.types.fibaro_smart_implant_cac_gt import ( FibaroSmartImplantCacGt, + FibaroSmartImplantCacGt_Maker, ) from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, @@ -55,43 +58,48 @@ ) from gwproto.types.hubitat_poller_cac_gt import ( HubitatPollerCacGt, + HubitatPollerCacGt_Maker, ) from gwproto.types.hubitat_poller_component_gt import ( HubitatPollerComponentGt, HubitatPollerComponentGt_Maker, ) -from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt +from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt, HubitatTankCacGt_Maker from gwproto.types.hubitat_tank_component_gt import ( HubitatTankComponentGt, HubitatTankComponentGt_Maker, ) from gwproto.types.multipurpose_sensor_cac_gt import ( MultipurposeSensorCacGt, + MultipurposeSensorCacGt_Maker, ) from gwproto.types.pipe_flow_sensor_cac_gt import ( PipeFlowSensorCacGt, + PipeFlowSensorCacGt_Maker, ) from gwproto.types.pipe_flow_sensor_component_gt import ( PipeFlowSensorComponentGt, PipeFlowSensorComponentGt_Maker, ) from gwproto.types.power_watts import PowerWatts, PowerWatts_Maker -from gwproto.types.relay_cac_gt import RelayCacGt +from gwproto.types.relay_cac_gt import RelayCacGt, RelayCacGt_Maker from gwproto.types.relay_component_gt import RelayComponentGt, RelayComponentGt_Maker from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, + ResistiveHeaterCacGt_Maker, ) from gwproto.types.resistive_heater_component_gt import ( ResistiveHeaterComponentGt, ResistiveHeaterComponentGt_Maker, ) -from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt +from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt, RESTPollerCacGt_Maker from gwproto.types.rest_poller_component_gt import ( RESTPollerComponentGt, RESTPollerComponentGt_Maker, ) from gwproto.types.simple_temp_sensor_cac_gt import ( SimpleTempSensorCacGt, + SimpleTempSensorCacGt_Maker, ) from gwproto.types.simple_temp_sensor_component_gt import ( SimpleTempSensorComponentGt, @@ -112,6 +120,7 @@ __all__ = [ "ComponentAttributeClassGt", + "ComponentAttributeClassGt_Maker", "ComponentGt", "ComponentGt_Maker", "DataChannel", @@ -121,9 +130,11 @@ "EgaugeRegisterConfig", "EgaugeRegisterConfig_Maker", "ElectricMeterCacGt", + "ElectricMeterCacGt_Maker", # "ElectricMeterComponentGt", # "ElectricMeterComponentGt_Maker", "FibaroSmartImplantCacGt", + "FibaroSmartImplantCacGt_Maker", "FibaroSmartImplantComponentGt", "FibaroSmartImplantComponentGt_Maker", "GtDispatchBoolean", @@ -151,29 +162,37 @@ "HubitatCacGt", "HubitatComponentGt", "HubitatPollerCacGt", + "HubitatPollerCacGt_Maker", "HubitatPollerComponentGt", "HubitatPollerComponentGt_Maker", "HubitatTankCacGt", + "HubitatTankCacGt_Maker", "HubitatTankComponentGt", "HubitatTankComponentGt_Maker", "MultipurposeSensorCacGt", + "MultipurposeSensorCacGt_Maker", # "MultipurposeSensorComponentGt", # "MultipurposeSensorComponentGt_Maker", "PipeFlowSensorCacGt", + "PipeFlowSensorCacGt_Maker", "PipeFlowSensorComponentGt", "PipeFlowSensorComponentGt_Maker", "PowerWatts", "PowerWatts_Maker", "RESTPollerCacGt", + "RESTPollerCacGt_Maker", "RESTPollerComponentGt", "RESTPollerComponentGt_Maker", "RelayCacGt", + "RelayCacGt_Maker", "RelayComponentGt", "RelayComponentGt_Maker", "ResistiveHeaterCacGt", + "ResistiveHeaterCacGt_Maker", "ResistiveHeaterComponentGt", "ResistiveHeaterComponentGt_Maker", "SimpleTempSensorCacGt", + "SimpleTempSensorCacGt_Maker", "SimpleTempSensorComponentGt", "SimpleTempSensorComponentGt_Maker", "SnapshotSpaceheat", diff --git a/src/gwproto/types/cacs.py b/src/gwproto/types/cacs.py deleted file mode 100644 index ee1f16e8..00000000 --- a/src/gwproto/types/cacs.py +++ /dev/null @@ -1,27 +0,0 @@ -from gwproto.types.electric_meter_cac_gt import ElectricMeterCacGt -from gwproto.types.fibaro_smart_implant_cac_gt import FibaroSmartImplantCacGt -from gwproto.types.hubitat_cac_gt import HubitatCacGt -from gwproto.types.hubitat_poller_cac_gt import HubitatPollerCacGt -from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt -from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt -from gwproto.types.pipe_flow_sensor_cac_gt import PipeFlowSensorCacGt -from gwproto.types.relay_cac_gt import RelayCacGt -from gwproto.types.resistive_heater_cac_gt import ResistiveHeaterCacGt -from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt -from gwproto.types.simple_temp_sensor_cac_gt import SimpleTempSensorCacGt -from gwproto.types.web_server_cac_gt import WebServerCacGt - -__all__ = [ - "ElectricMeterCacGt", - "FibaroSmartImplantCacGt", - "HubitatCacGt", - "HubitatPollerCacGt", - "HubitatTankCacGt", - "MultipurposeSensorCacGt", - "PipeFlowSensorCacGt", - "RESTPollerCacGt", - "RelayCacGt", - "ResistiveHeaterCacGt", - "SimpleTempSensorCacGt", - "WebServerCacGt", -] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index b3528774..ed522bda 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -1,49 +1,250 @@ """Type component.attribute.class.gt, version 000""" -from typing import Any, Callable, Dict, Literal, Optional - -from pydantic import ( - BaseModel, - ConfigDict, - SerializationInfo, - model_serializer, - model_validator, -) +import json +import logging +from typing import Any, Dict, Literal, Optional + +from pydantic import BaseModel, Field, field_validator -from gwproto.enums import MakeModel -from gwproto.enums.symbolized import desymbolize, symbolize, symbolizing -from gwproto.utils import UUID4Str +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass +from gwproto.errors import SchemaError + +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) class ComponentAttributeClassGt(BaseModel): - ComponentAttributeClassId: UUID4Str - DisplayName: Optional[str] = None - MakeModel: MakeModel + """ + Component Attribute Class Gt. + + Authority for the attributes of the component.attribute.class.gt.000 belongs to the WorldRegistry. + The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational + device data. Generally speaking, a component attribute class is meant to specify WHAT you + might order from a plumbing supply store to 'get the same part.' The Component refers to + something that will have a specific serial number. + + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ + + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "This identifier is used to associate a make/model with a specific component (i.e. " + "the component will point to its ComponentAttributeClassId)." + ), + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + description=( + "Optional Mutable field to include manufacturer's model name. Note that several different " + "models may be given the same spaceheat.make.model enum name." + ), + default=None, + ) TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" - model_config = ConfigDict( - extra="allow", - # comment these out to cut down on experimental headaches - # alias_generator=camel_to_snake, # noqa: ERA001 - # populate_by_name=True # noqa: ERA001 - use_enum_values=True, - ) + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + component.attribute.class.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + component.attribute.class.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + return { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"}, + by_alias=True, + ).items() + if value is not None + } + + def as_type(self) -> bytes: + """ + Serialize to the component.attribute.class.gt.000 representation. + + Instances in the class are python-native representations of component.attribute.class.gt.000 + objects, while the actual component.attribute.class.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is ComponentAttributeClassGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class ComponentAttributeClassGt_Maker: + type_name = "component.attribute.class.gt" + version = "000" + + def __init__( + self, + component_attribute_class_id: str, + display_name: Optional[str], + ) -> None: + self.tuple = ComponentAttributeClassGt( + ComponentAttributeClassId=component_attribute_class_id, + DisplayName=display_name, + ) - @model_validator(mode="before") @classmethod - def desymbolize(cls, data: Any) -> Any: - if symbolizing(): - desymbolize( - data, - symbolized_name="MakeModelGtEnumSymbol", - enum_class=MakeModel, + def tuple_to_type(cls, tuple_: ComponentAttributeClassGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tuple_.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> ComponentAttributeClassGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentAttributeClassGt: + """ + Deserialize a dictionary representation of a component.attribute.class.gt.000 message object + into a ComponentAttributeClassGt python object for internal use. + + This is the near-inverse of the ComponentAttributeClassGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a ComponentAttributeClassGt object. + + Returns: + ComponentAttributeClassGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret component.attribute.class.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return ComponentAttributeClassGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: ComponentAttributeClassGt) -> ComponentAttributeClass: + if t.ComponentAttributeClassId in ComponentAttributeClass.by_id: + dc = ComponentAttributeClass.by_id[t.ComponentAttributeClassId] + else: + dc = ComponentAttributeClass( + component_attribute_class_id=t.ComponentAttributeClassId, + display_name=t.DisplayName, ) - return data - - @model_serializer(when_used="json", mode="wrap") - def symbolize(self, handler: Callable, info: SerializationInfo) -> Dict[str, Any]: # noqa: ANN001 - d = handler(self, info) - if symbolizing(): - symbolize(d, enum_class=MakeModel) - return d + return dc + + @classmethod + def dc_to_tuple(cls, dc: ComponentAttributeClass) -> ComponentAttributeClassGt: + return ComponentAttributeClassGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + display_name=dc.display_name, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> ComponentAttributeClass: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: ComponentAttributeClass) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> ComponentAttributeClass: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 63c44d45..0cdcef45 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -1,14 +1,337 @@ """Type electric.meter.cac.gt, version 000""" -from typing import Literal, Optional +import json +import logging +from typing import Any, Dict, List, Literal, Optional +from pydantic import BaseModel, Field, field_validator + +from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.enums import LocalCommInterface, TelemetryName -from gwproto.types import ComponentAttributeClassGt +from gwproto.enums import MakeModel as EnumMakeModel +from gwproto.errors import SchemaError + +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class ElectricMeterCacGt(BaseModel): + """ + Type for tracking Electric Meter ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ -class ElectricMeterCacGt(ComponentAttributeClassGt): - TelemetryNameList: list[TelemetryName] - PollPeriodMs: int - Interface: LocalCommInterface - DefaultBaud: Optional[int] = None + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + description=( + "The brand name identifier for the electric meter (what you would specify in order " + "to buy one)." + ), + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + description="Sample: EGauge 4030", + default=None, + ) + TelemetryNameList: List[TelemetryName] = Field( + title="TelemetryNames read by this power meter", + ) + PollPeriodMs: int = Field( + title="Poll Period in Milliseconds", + description=( + "Poll Period refers to the period of time between two readings by the local actor. " + "This is in contrast to Capture Period, which refers to the period between readings " + "that are sent up to the cloud (or otherwise saved for the long-term)." + "[More info](https://gridworks-protocol.readthedocs.io/en/latest/data-polling-capturing-transmitting.rst)" + ), + ) + Interface: LocalCommInterface = Field( + title="Interface", + ) + DefaultBaud: Optional[int] = Field( + title="To be used when the comms method requires a baud rate", + default=None, + ) TypeName: Literal["electric.meter.cac.gt"] = "electric.meter.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + electric.meter.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + electric.meter.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + del d["TelemetryNameList"] + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(str(elt.value)) + for elt in self.TelemetryNameList + ] + del d["Interface"] + d["InterfaceGtEnumSymbol"] = LocalCommInterface.value_to_symbol(self.Interface) + return d + + def as_type(self) -> bytes: + """ + Serialize to the electric.meter.cac.gt.000 representation. + + Instances in the class are python-native representations of electric.meter.cac.gt.000 + objects, while the actual electric.meter.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is ElectricMeterCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class ElectricMeterCacGt_Maker: + type_name = "electric.meter.cac.gt" + version = "000" + + def __init__( # noqa: PLR0913, PLR0917, RUF100 + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + display_name: Optional[str], + telemetry_name_list: List[TelemetryName], + poll_period_ms: int, + interface: LocalCommInterface, + default_baud: Optional[int], + ) -> None: + self.tuple = ElectricMeterCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + DisplayName=display_name, + TelemetryNameList=telemetry_name_list, + PollPeriodMs=poll_period_ms, + Interface=interface, + DefaultBaud=default_baud, + ) + + @classmethod + def tuple_to_type(cls, tpl: ElectricMeterCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> ElectricMeterCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterCacGt: # noqa: C901 + """ + Deserialize a dictionary representation of a electric.meter.cac.gt.000 message object + into a ElectricMeterCacGt python object for internal use. + + This is the near-inverse of the ElectricMeterCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a ElectricMeterCacGt object. + + Returns: + ElectricMeterCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "TelemetryNameList" not in d2: + raise SchemaError(f"dict <{d2}> missing TelemetryNameList") + if not isinstance(d2["TelemetryNameList"], List): + raise SchemaError("TelemetryNameList must be a List!") + telemetry_name_list = [] + for elt in d2["TelemetryNameList"]: + value = TelemetryName.symbol_to_value(elt) + telemetry_name_list.append(TelemetryName(value)) + d2["TelemetryNameList"] = telemetry_name_list + if "PollPeriodMs" not in d2: + raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") + if "InterfaceGtEnumSymbol" not in d2: + raise SchemaError(f"InterfaceGtEnumSymbol missing from dict <{d2}>") + value = LocalCommInterface.symbol_to_value(d2["InterfaceGtEnumSymbol"]) + d2["Interface"] = LocalCommInterface(value) + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret electric.meter.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return ElectricMeterCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: ElectricMeterCacGt) -> ElectricMeterCac: + if t.ComponentAttributeClassId in ElectricMeterCac.by_id: + dc = ElectricMeterCac.by_id[t.ComponentAttributeClassId] + else: + dc = ElectricMeterCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + display_name=t.DisplayName, + telemetry_name_list=t.TelemetryNameList, + poll_period_ms=t.PollPeriodMs, + interface=t.Interface, + default_baud=t.DefaultBaud, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: ElectricMeterCac) -> ElectricMeterCacGt: + return ElectricMeterCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + display_name=dc.display_name, + telemetry_name_list=dc.telemetry_name_list, + poll_period_ms=dc.poll_period_ms, + interface=dc.interface, + default_baud=dc.default_baud, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> ElectricMeterCac: + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) + + @classmethod + def dc_to_type(cls, dc: ElectricMeterCac) -> str: + return cls.dc_to_tuple(dc).as_type().decode("utf-8") + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> ElectricMeterCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_positive_integer(v: int) -> None: + """ + Must be positive when interpreted as an integer. Interpretation as an + integer follows the pydantic rules for this - which will round down + rational numbers. So 1.7 will be interpreted as 1 and is also fine, + while 0.5 is interpreted as 0 and will raise an exception. + + Args: + v (int): the candidate + + Raises: + ValueError: if v < 1 + """ + v2 = int(v) + if v2 < 1: + raise ValueError(f"<{v}> is not PositiveInteger") + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py index c9106df3..e7c95d64 100644 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_cac_gt.py @@ -1,11 +1,76 @@ -from typing import Literal +import json +import typing +from typing import Any, Literal from pydantic import ConfigDict +from gwproto.data_classes.cacs.fibaro_smart_implant_cac import FibaroSmartImplantCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types import ComponentAttributeClassGt class FibaroSmartImplantCacGt(ComponentAttributeClassGt): Model: str = "" TypeName: Literal["fibaro.smart.implant.cac.gt"] = "fibaro.smart.implant.cac.gt" + Version: Literal["000"] = "000" model_config = ConfigDict(extra="allow") + + @classmethod + def from_data_class(cls, cac: FibaroSmartImplantCac) -> "FibaroSmartImplantCacGt": + return FibaroSmartImplantCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> FibaroSmartImplantCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(FibaroSmartImplantCac, cac) + return FibaroSmartImplantCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self), *tuple(self.__dict__.values()))) + + +class FibaroSmartImplantCacGt_Maker: + type_name: str = FibaroSmartImplantCacGt.model_fields["TypeName"].default + version = "000" + tuple: FibaroSmartImplantCacGt + + def __init__(self, cac: FibaroSmartImplantCac) -> None: + self.tuple = FibaroSmartImplantCacGt.from_data_class(cac) + + @classmethod + def tuple_to_type(cls, tpl: FibaroSmartImplantCacGt) -> str: + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: str) -> FibaroSmartImplantCacGt: + return cls.dict_to_tuple(json.loads(t)) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> FibaroSmartImplantCacGt: + return FibaroSmartImplantCacGt(**d) + + @classmethod + def tuple_to_dc(cls, t: FibaroSmartImplantCacGt) -> FibaroSmartImplantCac: + return t.to_data_class() + + @classmethod + def dc_to_tuple(cls, dc: FibaroSmartImplantCac) -> FibaroSmartImplantCacGt: + return FibaroSmartImplantCacGt.from_data_class(dc) + + @classmethod + def type_to_dc(cls, t: str) -> FibaroSmartImplantCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: FibaroSmartImplantCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> FibaroSmartImplantCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index b79d643a..b03f14b7 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -1,7 +1,30 @@ +import typing from typing import Literal +from gwproto.data_classes.cacs.hubitat_cac import HubitatCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.cac.gt"] = "hubitat.cac.gt" + Version: Literal["000"] = "000" + + @classmethod + def from_data_class(cls, cac: HubitatCac) -> "HubitatCacGt": + return HubitatCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> HubitatCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(HubitatCac, cac) + return HubitatCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self), *tuple(self.__dict__.values()))) diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index 538067b9..1032539c 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -1,7 +1,36 @@ +import typing from typing import Literal +from gwproto.data_classes.cacs.hubitat_cac import HubitatCac +from gwproto.data_classes.cacs.hubitat_poller_cac import HubitatPollerCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatPollerCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.poller.cac.gt"] = "hubitat.poller.cac.gt" + Version: Literal["000"] = "000" + + @classmethod + def from_data_class(cls, cac: HubitatCac) -> "HubitatPollerCacGt": + return HubitatPollerCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> HubitatPollerCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(HubitatPollerCac, cac) + return HubitatPollerCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self), *tuple(self.__dict__.values()))) + + +class HubitatPollerCacGt_Maker: + type_name = "hubitat.poller.cac.gt" + version = "000" diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index cc58132b..d238d3ef 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -1,7 +1,72 @@ +import json +import typing from typing import Literal +from gwproto.data_classes.cacs.hubitat_tank_module_cac import HubitatTankModuleCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class HubitatTankCacGt(ComponentAttributeClassGt): TypeName: Literal["hubitat.tank.cac.gt"] = "hubitat.tank.cac.gt" + Version: Literal["000"] = "000" + + @classmethod + def from_data_class(cls, cac: HubitatTankModuleCac) -> "HubitatTankCacGt": + return HubitatTankCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> HubitatTankModuleCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(HubitatTankModuleCac, cac) + return HubitatTankModuleCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self), *tuple(self.__dict__.values()))) + + +class HubitatTankCacGt_Maker: + type_name: str = HubitatTankCacGt.model_fields["TypeName"].default + version = "000" + tuple: HubitatTankCacGt + + def __init__(self, cac: HubitatTankModuleCac) -> None: + self.tuple = HubitatTankCacGt.from_data_class(cac) + + @classmethod + def tuple_to_type(cls, tpl: HubitatTankCacGt) -> str: + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: str) -> HubitatTankCacGt: + return cls.dict_to_tuple(json.loads(t)) + + @classmethod + def dict_to_tuple(cls, d: dict[str, typing.Any]) -> HubitatTankCacGt: + return HubitatTankCacGt(**d) + + @classmethod + def tuple_to_dc(cls, t: HubitatTankCacGt) -> HubitatTankModuleCac: + return t.to_data_class() + + @classmethod + def dc_to_tuple(cls, dc: HubitatTankModuleCac) -> HubitatTankCacGt: + return HubitatTankCacGt.from_data_class(dc) + + @classmethod + def type_to_dc(cls, t: str) -> HubitatTankModuleCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: HubitatTankModuleCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[typing.Any, str]) -> HubitatTankModuleCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py index a854a097..4989e5a4 100644 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ b/src/gwproto/types/multipurpose_sensor_cac_gt.py @@ -1,17 +1,340 @@ """Type multipurpose.sensor.cac.gt, version 000""" -from typing import Literal, Optional +import json +import logging +from typing import Any, Dict, List, Literal, Optional +from pydantic import BaseModel, Field, field_validator + +from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac +from gwproto.enums import MakeModel as EnumMakeModel from gwproto.enums import TelemetryName, Unit -from gwproto.types import ComponentAttributeClassGt +from gwproto.errors import SchemaError + +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class MultipurposeSensorCacGt(BaseModel): + """ + Type for tracking Multipuprose Sensor ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ -class MultipurposeSensorCacGt(ComponentAttributeClassGt): - PollPeriodMs: int - Exponent: int - TempUnit: Unit - TelemetryNameList: list[TelemetryName] - MaxThermistors: Optional[int] = None - DisplayName: Optional[str] = None - CommsMethod: Optional[str] = None + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + description=( + "Meant to be enough to articulate any difference in how GridWorks code would interact " + "with a device. Should be able to use this information to buy or build a device." + ), + ) + PollPeriodMs: int = Field( + title="Poll Period in Milliseconds", + description=( + "Poll Period refers to the period of time between two readings by the local actor. " + "This is in contrast to Capture Period, which refers to the period between readings " + "that are sent up to the cloud (or otherwise saved for the long-term)." + "[More info](https://gridworks-protocol.readthedocs.io/en/latest/data-polling-capturing-transmitting.rst)" + ), + ) + Exponent: int = Field( + title="Exponent", + description=( + "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " + "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " + "would indicate 65.3 deg C" + ), + ) + TempUnit: Unit = Field( + title="Temp Unit", + ) + TelemetryNameList: List[TelemetryName] = Field( + title="TelemetryNameList", + ) + MaxThermistors: Optional[int] = Field( + title="MaxThermistors", + description="The maximum number of temperature sensors this multipurpose sensor can read.", + default=None, + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + description="Sample: GridWorks TSnap1.0 as 12-channel analog temp sensor", + default=None, + ) + CommsMethod: Optional[str] = Field( + title="CommsMethod", + default=None, + ) TypeName: Literal["multipurpose.sensor.cac.gt"] = "multipurpose.sensor.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + multipurpose.sensor.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + multipurpose.sensor.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + del d["TempUnit"] + d["TempUnitGtEnumSymbol"] = Unit.value_to_symbol(self.TempUnit) + d["TelemetryNameList"] = [ + TelemetryName.value_to_symbol(elt) for elt in self.TelemetryNameList + ] + return d + + def as_type(self) -> bytes: + """ + Serialize to the multipurpose.sensor.cac.gt.000 representation. + + Instances in the class are python-native representations of multipurpose.sensor.cac.gt.000 + objects, while the actual multipurpose.sensor.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is MultipurposeSensorCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class MultipurposeSensorCacGt_Maker: + type_name = "multipurpose.sensor.cac.gt" + version = "000" + + def __init__( # noqa: PLR0913, PLR0917, RUF100 + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + poll_period_ms: int, + exponent: int, + temp_unit: Unit, + telemetry_name_list: List[TelemetryName], + max_thermistors: Optional[int], + display_name: Optional[str], + comms_method: Optional[str], + ) -> None: + self.tuple = MultipurposeSensorCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + PollPeriodMs=poll_period_ms, + Exponent=exponent, + TempUnit=temp_unit, + TelemetryNameList=telemetry_name_list, + MaxThermistors=max_thermistors, + DisplayName=display_name, + CommsMethod=comms_method, + ) + + @classmethod + def tuple_to_type(cls, tpl: MultipurposeSensorCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> MultipurposeSensorCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorCacGt: # noqa: C901 + """ + Deserialize a dictionary representation of a multipurpose.sensor.cac.gt.000 message object + into a MultipurposeSensorCacGt python object for internal use. + + This is the near-inverse of the MultipurposeSensorCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a MultipurposeSensorCacGt object. + + Returns: + MultipurposeSensorCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "PollPeriodMs" not in d2: + raise SchemaError(f"dict missing PollPeriodMs: <{d2}>") + if "Exponent" not in d2: + raise SchemaError(f"dict missing Exponent: <{d2}>") + if "TempUnitGtEnumSymbol" not in d2: + raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") + value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) + d2["TempUnit"] = Unit(value) + if "TelemetryNameList" not in d2: + raise SchemaError(f"dict <{d2}> missing TelemetryNameList") + if not isinstance(d2["TelemetryNameList"], List): + raise SchemaError("TelemetryNameList must be a List!") + telemetry_name_list = [] + for elt in d2["TelemetryNameList"]: + value = TelemetryName.symbol_to_value(elt) + telemetry_name_list.append(TelemetryName(value)) + d2["TelemetryNameList"] = telemetry_name_list + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret multipurpose.sensor.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return MultipurposeSensorCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: MultipurposeSensorCacGt) -> MultipurposeSensorCac: + if t.ComponentAttributeClassId in MultipurposeSensorCac.by_id: + dc = MultipurposeSensorCac.by_id[t.ComponentAttributeClassId] + else: + dc = MultipurposeSensorCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + poll_period_ms=t.PollPeriodMs, + exponent=t.Exponent, + temp_unit=t.TempUnit, + telemetry_name_list=t.TelemetryNameList, + max_thermistors=t.MaxThermistors, + display_name=t.DisplayName, + comms_method=t.CommsMethod, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: MultipurposeSensorCac) -> MultipurposeSensorCacGt: + return MultipurposeSensorCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + poll_period_ms=dc.poll_period_ms, + exponent=dc.exponent, + temp_unit=dc.temp_unit, + telemetry_name_list=dc.telemetry_name_list, + max_thermistors=dc.max_thermistors, + display_name=dc.display_name, + comms_method=dc.comms_method, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> MultipurposeSensorCac: + return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) + + @classmethod + def dc_to_type(cls, dc: MultipurposeSensorCac) -> str: + return cls.dc_to_tuple(dc).as_type().decode("utf-8") + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> MultipurposeSensorCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py index e933937f..959ebe41 100644 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_cac_gt.py @@ -1,10 +1,265 @@ """Type pipe.flow.sensor.cac.gt, version 000""" -from typing import Literal, Optional +import json +import logging +from typing import Any, Dict, Literal, Optional -from gwproto.types import ComponentAttributeClassGt +from pydantic import BaseModel, Field, field_validator +from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac +from gwproto.enums import MakeModel as EnumMakeModel +from gwproto.errors import SchemaError -class PipeFlowSensorCacGt(ComponentAttributeClassGt): - CommsMethod: Optional[str] = None +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class PipeFlowSensorCacGt(BaseModel): + """ + Type for tracking Pipe Flow Sensor ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ + + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + description="Sample: Atlas Scientific EZO FLO i2c", + default=None, + ) + CommsMethod: Optional[str] = Field( + title="CommsMethod", + default=None, + ) TypeName: Literal["pipe.flow.sensor.cac.gt"] = "pipe.flow.sensor.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + pipe.flow.sensor.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + pipe.flow.sensor.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + return d + + def as_type(self) -> bytes: + """ + Serialize to the pipe.flow.sensor.cac.gt.000 representation. + + Instances in the class are python-native representations of pipe.flow.sensor.cac.gt.000 + objects, while the actual pipe.flow.sensor.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is PipeFlowSensorCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class PipeFlowSensorCacGt_Maker: + type_name = "pipe.flow.sensor.cac.gt" + version = "000" + + def __init__( + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + display_name: Optional[str], + comms_method: Optional[str], + ) -> None: + self.tuple = PipeFlowSensorCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + DisplayName=display_name, + CommsMethod=comms_method, + ) + + @classmethod + def tuple_to_type(cls, tpl: PipeFlowSensorCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> PipeFlowSensorCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorCacGt: + """ + Deserialize a dictionary representation of a pipe.flow.sensor.cac.gt.000 message object + into a PipeFlowSensorCacGt python object for internal use. + + This is the near-inverse of the PipeFlowSensorCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a PipeFlowSensorCacGt object. + + Returns: + PipeFlowSensorCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret pipe.flow.sensor.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return PipeFlowSensorCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: PipeFlowSensorCacGt) -> PipeFlowSensorCac: + if t.ComponentAttributeClassId in PipeFlowSensorCac.by_id: + dc = PipeFlowSensorCac.by_id[t.ComponentAttributeClassId] + else: + dc = PipeFlowSensorCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + display_name=t.DisplayName, + comms_method=t.CommsMethod, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: PipeFlowSensorCac) -> PipeFlowSensorCacGt: + return PipeFlowSensorCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + display_name=dc.display_name, + comms_method=dc.comms_method, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> PipeFlowSensorCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: PipeFlowSensorCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> PipeFlowSensorCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py index 3c61d98a..27e8b012 100644 --- a/src/gwproto/types/relay_cac_gt.py +++ b/src/gwproto/types/relay_cac_gt.py @@ -1,10 +1,265 @@ """Type relay.cac.gt, version 000""" -from typing import Literal +import json +import logging +from typing import Any, Dict, Literal, Optional -from gwproto.types import ComponentAttributeClassGt +from pydantic import BaseModel, Field, field_validator +from gwproto.data_classes.cacs.relay_cac import RelayCac +from gwproto.enums import MakeModel as EnumMakeModel +from gwproto.errors import SchemaError -class RelayCacGt(ComponentAttributeClassGt): - TypicalResponseTimeMs: int +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class RelayCacGt(BaseModel): + """ + Type for tracking Relay ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ + + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + default=None, + ) + TypicalResponseTimeMs: int = Field( + title="TypicalResponseTimeMs", + ) TypeName: Literal["relay.cac.gt"] = "relay.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + relay.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + relay.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + return d + + def as_type(self) -> bytes: + """ + Serialize to the relay.cac.gt.000 representation. + + Instances in the class are python-native representations of relay.cac.gt.000 + objects, while the actual relay.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is RelayCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class RelayCacGt_Maker: + type_name = "relay.cac.gt" + version = "000" + + def __init__( + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + display_name: Optional[str], + typical_response_time_ms: int, + ) -> None: + self.tuple = RelayCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + DisplayName=display_name, + TypicalResponseTimeMs=typical_response_time_ms, + ) + + @classmethod + def tuple_to_type(cls, tpl: RelayCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> RelayCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> RelayCacGt: + """ + Deserialize a dictionary representation of a relay.cac.gt.000 message object + into a RelayCacGt python object for internal use. + + This is the near-inverse of the RelayCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a RelayCacGt object. + + Returns: + RelayCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "TypicalResponseTimeMs" not in d2: + raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret relay.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return RelayCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: RelayCacGt) -> RelayCac: + if t.ComponentAttributeClassId in RelayCac.by_id: + dc = RelayCac.by_id[t.ComponentAttributeClassId] + else: + dc = RelayCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + display_name=t.DisplayName, + typical_response_time_ms=t.TypicalResponseTimeMs, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: RelayCac) -> RelayCacGt: + return RelayCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + display_name=dc.display_name, + typical_response_time_ms=dc.typical_response_time_ms, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> RelayCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: RelayCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> RelayCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/resistive_heater_cac_gt.py b/src/gwproto/types/resistive_heater_cac_gt.py index 8df08fa7..0d177037 100644 --- a/src/gwproto/types/resistive_heater_cac_gt.py +++ b/src/gwproto/types/resistive_heater_cac_gt.py @@ -1,11 +1,303 @@ """Type resistive.heater.cac.gt, version 000""" -from typing import Literal +import json +import logging +from typing import Any, Dict, Literal, Optional -from gwproto.types import ComponentAttributeClassGt +from pydantic import BaseModel, Field, field_validator +from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac +from gwproto.enums import MakeModel as EnumMakeModel +from gwproto.errors import SchemaError -class ResistiveHeaterCacGt(ComponentAttributeClassGt): - NameplateMaxPowerW: int - RatedVoltageV: int +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class ResistiveHeaterCacGt(BaseModel): + """ + Type for tracking Resistive Heater ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ + + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + default=None, + ) + NameplateMaxPowerW: int = Field( + title="NameplateMaxPowerW", + ) + RatedVoltageV: int = Field( + title="RatedVoltageV", + ) TypeName: Literal["resistive.heater.cac.gt"] = "resistive.heater.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + @field_validator("RatedVoltageV") + @classmethod + def _check_rated_voltage_v(cls, v: int) -> int: + try: + check_is_positive_integer(v) + except ValueError as e: + raise ValueError( + f"RatedVoltageV failed PositiveInteger format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + resistive.heater.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + resistive.heater.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + return d + + def as_type(self) -> bytes: + """ + Serialize to the resistive.heater.cac.gt.000 representation. + + Instances in the class are python-native representations of resistive.heater.cac.gt.000 + objects, while the actual resistive.heater.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is ResistiveHeaterCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class ResistiveHeaterCacGt_Maker: + type_name = "resistive.heater.cac.gt" + version = "000" + + def __init__( + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + display_name: Optional[str], + nameplate_max_power_w: int, + rated_voltage_v: int, + ) -> None: + self.tuple = ResistiveHeaterCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + DisplayName=display_name, + NameplateMaxPowerW=nameplate_max_power_w, + RatedVoltageV=rated_voltage_v, + ) + + @classmethod + def tuple_to_type(cls, tpl: ResistiveHeaterCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> ResistiveHeaterCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterCacGt: + """ + Deserialize a dictionary representation of a resistive.heater.cac.gt.000 message object + into a ResistiveHeaterCacGt python object for internal use. + + This is the near-inverse of the ResistiveHeaterCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a ResistiveHeaterCacGt object. + + Returns: + ResistiveHeaterCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "NameplateMaxPowerW" not in d2: + raise SchemaError(f"dict missing NameplateMaxPowerW: <{d2}>") + if "RatedVoltageV" not in d2: + raise SchemaError(f"dict missing RatedVoltageV: <{d2}>") + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret resistive.heater.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return ResistiveHeaterCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: ResistiveHeaterCacGt) -> ResistiveHeaterCac: + if t.ComponentAttributeClassId in ResistiveHeaterCac.by_id: + dc = ResistiveHeaterCac.by_id[t.ComponentAttributeClassId] + else: + dc = ResistiveHeaterCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + display_name=t.DisplayName, + nameplate_max_power_w=t.NameplateMaxPowerW, + rated_voltage_v=t.RatedVoltageV, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: ResistiveHeaterCac) -> ResistiveHeaterCacGt: + return ResistiveHeaterCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + display_name=dc.display_name, + nameplate_max_power_w=dc.nameplate_max_power_w, + rated_voltage_v=dc.rated_voltage_v, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> ResistiveHeaterCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: ResistiveHeaterCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> ResistiveHeaterCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_positive_integer(v: int) -> None: + """ + Must be positive when interpreted as an integer. Interpretation as an + integer follows the pydantic rules for this - which will round down + rational numbers. So 1.7 will be interpreted as 1 and is also fine, + while 0.5 is interpreted as 0 and will raise an exception. + + Args: + v (int): the candidate + + Raises: + ValueError: if v < 1 + """ + v2 = int(v) + if v2 < 1: + raise ValueError(f"<{v}> is not PositiveInteger") + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py index ad642855..bca855f1 100644 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ b/src/gwproto/types/rest_poller_cac_gt.py @@ -1,7 +1,72 @@ -from typing import Literal +import json +import typing +from typing import Any, Literal +from gwproto.data_classes.cacs.rest_poller_cac import RESTPollerCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types import ComponentAttributeClassGt class RESTPollerCacGt(ComponentAttributeClassGt): TypeName: Literal["rest.poller.cac.gt"] = "rest.poller.cac.gt" + Version: Literal["000"] = "000" + + @classmethod + def from_data_class(cls, cac: RESTPollerCac) -> "RESTPollerCacGt": + return RESTPollerCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> RESTPollerCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(RESTPollerCac, cac) + return RESTPollerCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self), *tuple(self.__dict__.values()))) + + +class RESTPollerCacGt_Maker: + type_name: str = RESTPollerCacGt.model_fields["TypeName"].default + version = "000" + tuple: RESTPollerCacGt + + def __init__(self, cac: RESTPollerCac) -> None: + self.tuple = RESTPollerCacGt.from_data_class(cac) + + @classmethod + def tuple_to_type(cls, tpl: RESTPollerCacGt) -> str: + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: str) -> RESTPollerCacGt: + return cls.dict_to_tuple(json.loads(t)) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> RESTPollerCacGt: + return RESTPollerCacGt(**d) + + @classmethod + def tuple_to_dc(cls, t: RESTPollerCacGt) -> RESTPollerCac: + return t.to_data_class() + + @classmethod + def dc_to_tuple(cls, dc: RESTPollerCac) -> RESTPollerCacGt: + return RESTPollerCacGt.from_data_class(dc) + + @classmethod + def type_to_dc(cls, t: str) -> RESTPollerCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: RESTPollerCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py index 47159c72..6af6864b 100644 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ b/src/gwproto/types/simple_temp_sensor_cac_gt.py @@ -1,15 +1,317 @@ """Type simple.temp.sensor.cac.gt, version 000""" -from typing import Literal, Optional +import json +import logging +from typing import Any, Dict, Literal, Optional -from gwproto.enums import TelemetryName, Unit -from gwproto.types import ComponentAttributeClassGt +from pydantic import BaseModel, Field, field_validator +from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac +from gwproto.enums import MakeModel as EnumMakeModel +from gwproto.enums import TelemetryName as EnumTelemetryName +from gwproto.enums import Unit +from gwproto.errors import SchemaError -class SimpleTempSensorCacGt(ComponentAttributeClassGt): - TypicalResponseTimeMs: int - Exponent: int - TempUnit: Unit - TelemetryName: TelemetryName - CommsMethod: Optional[str] = None +LOG_FORMAT = ( + "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " + "-35s %(lineno) -5d: %(message)s" +) +LOGGER = logging.getLogger(__name__) + + +class SimpleTempSensorCacGt(BaseModel): + """ + Type for tracking Simple Temp Sensor ComponentAttributeClasses. + + GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for + managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure. + + [More info](https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html) + """ + + ComponentAttributeClassId: str = Field( + title="ComponentAttributeClassId", + description=( + "Unique identifier for the device class (aka 'cac' or Component Attribute Class). " + "Authority is maintained by the World Registry." + ), + ) + MakeModel: EnumMakeModel = Field( + title="MakeModel", + ) + TypicalResponseTimeMs: int = Field( + title="TypicalResponseTimeMs", + ) + Exponent: int = Field( + title="Exponent", + description=( + "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " + "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " + "would indicate 65.3 deg C" + ), + ) + TempUnit: Unit = Field( + title="TempUnit", + ) + TelemetryName: EnumTelemetryName = Field( + title="TelemetryName", + ) + DisplayName: Optional[str] = Field( + title="DisplayName", + default=None, + ) + CommsMethod: Optional[str] = Field( + title="CommsMethod", + default=None, + ) TypeName: Literal["simple.temp.sensor.cac.gt"] = "simple.temp.sensor.cac.gt" + Version: Literal["000"] = "000" + + @field_validator("ComponentAttributeClassId") + @classmethod + def _check_component_attribute_class_id(cls, v: str) -> str: + try: + check_is_uuid_canonical_textual(v) + except ValueError as e: + raise ValueError( + f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" + ) + return v + + def as_dict(self) -> Dict[str, Any]: + """ + Translate the object into a dictionary representation that can be serialized into a + simple.temp.sensor.cac.gt.000 object. + + This method prepares the object for serialization by the as_type method, creating a + dictionary with key-value pairs that follow the requirements for an instance of the + simple.temp.sensor.cac.gt.000 type. Unlike the standard python dict method, + it makes the following substantive changes: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + """ + d = { + key: value + for key, value in self.model_dump( + include=self.model_fields_set | {"TypeName", "Version"} + ).items() + if value is not None + } + del d["MakeModel"] + d["MakeModelGtEnumSymbol"] = EnumMakeModel.value_to_symbol(self.MakeModel) + del d["TempUnit"] + d["TempUnitGtEnumSymbol"] = Unit.value_to_symbol(self.TempUnit) + del d["TelemetryName"] + d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( + self.TelemetryName + ) + return d + + def as_type(self) -> bytes: + """ + Serialize to the simple.temp.sensor.cac.gt.000 representation. + + Instances in the class are python-native representations of simple.temp.sensor.cac.gt.000 + objects, while the actual simple.temp.sensor.cac.gt.000 object is the serialized UTF-8 byte + string designed for sending in a message. + + This method calls the as_dict() method, which differs from the native python dict() + in the following key ways: + - Enum Values: Translates between the values used locally by the actor to the symbol + sent in messages. + - - Removes any key-value pairs where the value is None for a clearer message, especially + in cases with many optional attributes. + + It also applies these changes recursively to sub-types. + + Its near-inverse is SimpleTempSensorCacGt.type_to_tuple(). If the type (or any sub-types) + includes an enum, then the type_to_tuple will map an unrecognized symbol to the + default enum value. This is why these two methods are only 'near' inverses. + """ + json_string = json.dumps(self.as_dict()) + return json_string.encode("utf-8") + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa + + +class SimpleTempSensorCacGt_Maker: + type_name = "simple.temp.sensor.cac.gt" + version = "000" + + def __init__( # noqa: PLR0913, PLR0917, RUF100 + self, + component_attribute_class_id: str, + make_model: EnumMakeModel, + typical_response_time_ms: int, + exponent: int, + temp_unit: Unit, + telemetry_name: EnumTelemetryName, + display_name: Optional[str], + comms_method: Optional[str], + ) -> None: + self.tuple = SimpleTempSensorCacGt( + ComponentAttributeClassId=component_attribute_class_id, + MakeModel=make_model, + TypicalResponseTimeMs=typical_response_time_ms, + Exponent=exponent, + TempUnit=temp_unit, + TelemetryName=telemetry_name, + DisplayName=display_name, + CommsMethod=comms_method, + ) + + @classmethod + def tuple_to_type(cls, tpl: SimpleTempSensorCacGt) -> bytes: + """ + Given a Python class object, returns the serialized JSON type object. + """ + return tpl.as_type() + + @classmethod + def type_to_tuple(cls, t: bytes) -> SimpleTempSensorCacGt: + """ + Given a serialized JSON type object, returns the Python class object. + """ + try: + d = json.loads(t) + except TypeError: + raise SchemaError("Type must be string or bytes!") + if not isinstance(d, dict): + raise SchemaError(f"Deserializing <{t}> must result in dict!") + return cls.dict_to_tuple(d) + + @classmethod + def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorCacGt: + """ + Deserialize a dictionary representation of a simple.temp.sensor.cac.gt.000 message object + into a SimpleTempSensorCacGt python object for internal use. + + This is the near-inverse of the SimpleTempSensorCacGt.as_dict() method: + - Enums: translates between the symbols sent in messages between actors and + the values used by the actors internally once they've deserialized the messages. + - Types: recursively validates and deserializes sub-types. + + Note that if a required attribute with a default value is missing in a dict, this method will + raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing + missing attributes with default values when they exist. + + Args: + d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. + + Raises: + SchemaError: if the dict cannot be turned into a SimpleTempSensorCacGt object. + + Returns: + SimpleTempSensorCacGt + """ + d2 = dict(d) + if "ComponentAttributeClassId" not in d2: + raise SchemaError(f"dict missing ComponentAttributeClassId: <{d2}>") + if "MakeModelGtEnumSymbol" not in d2: + raise SchemaError(f"MakeModelGtEnumSymbol missing from dict <{d2}>") + value = EnumMakeModel.symbol_to_value(d2["MakeModelGtEnumSymbol"]) + d2["MakeModel"] = EnumMakeModel(value) + if "TypicalResponseTimeMs" not in d2: + raise SchemaError(f"dict missing TypicalResponseTimeMs: <{d2}>") + if "Exponent" not in d2: + raise SchemaError(f"dict missing Exponent: <{d2}>") + if "TempUnitGtEnumSymbol" not in d2: + raise SchemaError(f"TempUnitGtEnumSymbol missing from dict <{d2}>") + value = Unit.symbol_to_value(d2["TempUnitGtEnumSymbol"]) + d2["TempUnit"] = Unit(value) + if "TelemetryNameGtEnumSymbol" not in d2: + raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") + value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) + d2["TelemetryName"] = EnumTelemetryName(value) + if "TypeName" not in d2: + raise SchemaError(f"TypeName missing from dict <{d2}>") + if "Version" not in d2: + raise SchemaError(f"Version missing from dict <{d2}>") + if d2["Version"] != "000": + LOGGER.debug( + f"Attempting to interpret simple.temp.sensor.cac.gt version {d2['Version']} as version 000" + ) + d2["Version"] = "000" + return SimpleTempSensorCacGt(**d2) + + @classmethod + def tuple_to_dc(cls, t: SimpleTempSensorCacGt) -> SimpleTempSensorCac: + if t.ComponentAttributeClassId in SimpleTempSensorCac.by_id: + dc = SimpleTempSensorCac.by_id[t.ComponentAttributeClassId] + else: + dc = SimpleTempSensorCac( + component_attribute_class_id=t.ComponentAttributeClassId, + make_model=t.MakeModel, + typical_response_time_ms=t.TypicalResponseTimeMs, + exponent=t.Exponent, + temp_unit=t.TempUnit, + telemetry_name=t.TelemetryName, + display_name=t.DisplayName, + comms_method=t.CommsMethod, + ) + return dc + + @classmethod + def dc_to_tuple(cls, dc: SimpleTempSensorCac) -> SimpleTempSensorCacGt: + return SimpleTempSensorCacGt_Maker( + component_attribute_class_id=dc.component_attribute_class_id, + make_model=dc.make_model, + typical_response_time_ms=dc.typical_response_time_ms, + exponent=dc.exponent, + temp_unit=dc.temp_unit, + telemetry_name=dc.telemetry_name, + display_name=dc.display_name, + comms_method=dc.comms_method, + ).tuple + + @classmethod + def type_to_dc(cls, t: str) -> SimpleTempSensorCac: + return cls.tuple_to_dc(cls.type_to_tuple(t)) + + @classmethod + def dc_to_type(cls, dc: SimpleTempSensorCac) -> str: + return cls.dc_to_tuple(dc).as_type() + + @classmethod + def dict_to_dc(cls, d: dict[Any, str]) -> SimpleTempSensorCac: + return cls.tuple_to_dc(cls.dict_to_tuple(d)) + + +def check_is_uuid_canonical_textual(v: str) -> None: + """Checks UuidCanonicalTextual format + + UuidCanonicalTextual format: A string of hex words separated by hyphens + of length 8-4-4-4-12. + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not UuidCanonicalTextual format + """ + try: + x = v.split("-") + except AttributeError as e: + raise ValueError(f"Failed to split on -: {e}") + if len(x) != 5: + raise ValueError(f"<{v}> split by '-' did not have 5 words") + for hex_word in x: + try: + int(hex_word, 16) + except ValueError: # noqa: PERF203 + raise ValueError(f"Words of <{v}> are not all hex") + if len(x[0]) != 8: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[1]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[2]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[3]) != 4: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + if len(x[4]) != 12: + raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index be1674d1..ddc17579 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -1,7 +1,30 @@ +import typing from typing import Literal +from gwproto.data_classes.cacs.web_server_cac import WebServerCac +from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt class WebServerCacGt(ComponentAttributeClassGt): TypeName: Literal["web.server.cac.gt"] = "web.server.cac.gt" + Version: Literal["000"] = "000" + + @classmethod + def from_data_class(cls, cac: WebServerCac) -> "WebServerCacGt": + return WebServerCacGt( + ComponentAttributeClassId=cac.component_attribute_class_id, + DisplayName=cac.display_name, + ) + + def to_data_class(self) -> WebServerCac: + cac = ComponentAttributeClass.by_id.get(self.ComponentAttributeClassId, None) + if cac is not None: + return typing.cast(WebServerCac, cac) + return WebServerCac( + component_attribute_class_id=self.ComponentAttributeClassId, + display_name=self.DisplayName, + ) + + def __hash__(self) -> int: + return hash((type(self),) + tuple(self.__dict__.values())) # noqa diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index 4306e4de..eb2c18c2 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -1,8 +1,4 @@ import re -import uuid -from typing import Annotated - -from pydantic import BeforeValidator snake_add_underscore_to_camel_pattern = re.compile(r"(? str: def has_mac_address_format(mac_str: str) -> bool: return bool(MAC_REGEX.match(mac_str.lower())) - - -def str_is_valid_uuid4(v: str) -> str: - v = str(v) - try: - u = uuid.UUID(v) - except Exception as e: - raise ValueError(f"Invalid UUID4: {v} <{e}>") from e - if u.version != 4: - raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") - return str(u) - - -UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] diff --git a/tests/cac_load_utils.py b/tests/cac_load_utils.py deleted file mode 100644 index 0dd9bd6c..00000000 --- a/tests/cac_load_utils.py +++ /dev/null @@ -1,139 +0,0 @@ -import json -from dataclasses import dataclass, field -from typing import Any, Optional, Type - -from pydantic import BaseModel - -from gwproto import CacDecoder, default_cac_decoder -from gwproto.data_classes.hardware_layout import ( - load_cacs, -) -from gwproto.enums.symbolized import SYMBOLIZE_ENV_VAR -from gwproto.types import ComponentAttributeClassGt - - -@dataclass -class CacCase: - tag: str - src_cac: ComponentAttributeClassGt | dict - exp_cac_type: Type[ComponentAttributeClassGt] = ComponentAttributeClassGt - exp_cac: Optional[ComponentAttributeClassGt | dict] = None - exp_exceptions: list[Type[Exception]] = field(default_factory=list) - - -@dataclass -class CacCaseError: - case_idx: int - case: CacCase - - def __str__(self) -> str: - return f"{self.case.tag:30s} {self.case_idx:2d} {type(self)}" - - -@dataclass -class CacLoadError(CacCaseError): - exception: Exception - - def __str__(self) -> str: - return ( - f"{super().__str__()}" - f"\n\t\t{type(self.exception)}" - f"\n\t\t{self.exception}" - ) - - -@dataclass -class CacMatchError(CacCaseError): - exp_cac: ComponentAttributeClassGt | dict - loaded_cac: ComponentAttributeClassGt - - def __str__(self) -> str: - return ( - f"{super().__str__()}" - f"\n\t\texp: {type(self.exp_cac)}" - f"\n\t\tgot: {type(self.loaded_cac)}" - ) - - -@dataclass -class CacLoadResult: - ok: bool - loaded: ComponentAttributeClassGt | None - exception: Exception | None - - -def _encode_decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: - if decoder is None: - decoder = default_cac_decoder - # Force call to model_dump_json() by encoding dict as model to ensure - # serialization is tested. - if isinstance(case.src_cac, ComponentAttributeClassGt): - cac = case.src_cac - else: - cac = case.exp_cac_type.model_validate(case.src_cac) - cac_dict = json.loads(cac.model_dump_json()) - cac_id = cac_dict["ComponentAttributeClassId"] - try: - loaded_cac = load_cacs( - layout={"OtherCacs": [cac_dict]}, - raise_errors=True, - cac_decoder=decoder, - )[cac_id] - exception = None - except Exception as e: # noqa: BLE001 - loaded_cac = None - exception = e - if loaded_cac is None: - ok = type(exception) in case.exp_exceptions - else: - ok = not case.exp_exceptions - return CacLoadResult(ok, loaded_cac, exception) - - -def assert_cac_load(cases: list[CacCase], decoder: Optional[CacDecoder] = None) -> None: - errors: list[CacCaseError] = [] - for case_idx, case in enumerate(cases): - load_result = _encode_decode_cac(case, decoder) - if not load_result.ok: - errors.append(CacLoadError(case_idx, case, load_result.exception)) - elif not case.exp_exceptions: - exp_cac = case.src_cac if case.exp_cac is None else case.exp_cac - if isinstance(exp_cac, dict): - exp_cac = case.exp_cac_type(**exp_cac) - if load_result.loaded != exp_cac: - errors.append( - CacMatchError( - case_idx=case_idx, - case=case, - exp_cac=exp_cac, - loaded_cac=load_result.loaded, - ) - ) - if errors: - err_str = "ERROR. Got cac load/matching errors:" - first_exception = None - for error in errors: - err_str += f"\n\t{error}" - if first_exception is None and hasattr(error, "exception"): - first_exception = error.exception - if first_exception is not None: - raise ValueError(err_str) from first_exception - raise ValueError(err_str) - - -def assert_symbolized_and_unsymbolized_encode( - monkeypatch: Any, - m: BaseModel, - exp_unsymbolized: dict, - exp_symbolized: dict, -) -> None: - for symbolization, exp_dict in [ - ("0", exp_unsymbolized), - ("1", exp_symbolized), - ]: - with monkeypatch.context() as monkey: - monkey.setenv(SYMBOLIZE_ENV_VAR, symbolization) - d = json.loads(m.model_dump_json()) - assert d == exp_dict - m2 = m.__class__.model_validate(d) - assert m == m2 diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 3bf6f003..9bc51006 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -1,26 +1,26 @@ -# from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac -# from gwproto.data_classes.hardware_layout import HardwareLayout -# from gwproto.types import ElectricMeterCacGt_Maker -# -# # Running the below disrupts other tests. Need to set up the -# # test isolation as per scada -# -# -# def test_electric_meter_cac() -> None: -# HardwareLayout.load("tests/config/hardware-layout.json") -# d = { -# "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", -# "MakeModelGtEnumSymbol": "076da322", -# "DisplayName": "Gridworks Pm1 Simulated Power Meter", -# "InterfaceGtEnumSymbol": "efc144cd", -# "PollPeriodMs": 1000, -# "TelemetryNameList": ["af39eec9"], -# "TypeName": "electric.meter.cac.gt", -# "Version": "000", -# } -# -# gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) -# assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id -# dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] -# -# assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" +from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac +from gwproto.data_classes.hardware_layout import HardwareLayout +from gwproto.types import ElectricMeterCacGt_Maker + +# Running the below disrupts other tests. Need to set up the +# test isolation as per scada + + +def test_electric_meter_cac() -> None: + HardwareLayout.load("tests/config/hardware-layout.json") + d = { + "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", + "MakeModelGtEnumSymbol": "076da322", + "DisplayName": "Gridworks Pm1 Simulated Power Meter", + "InterfaceGtEnumSymbol": "efc144cd", + "PollPeriodMs": 1000, + "TelemetryNameList": ["af39eec9"], + "TypeName": "electric.meter.cac.gt", + "Version": "000", + } + + gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) + assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id + dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] + + assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" diff --git a/tests/data_classes/test_electric_meter_component.py b/tests/data_classes/test_electric_meter_component.py index 8e363020..126c9837 100644 --- a/tests/data_classes/test_electric_meter_component.py +++ b/tests/data_classes/test_electric_meter_component.py @@ -9,10 +9,7 @@ def test_electric_meter_component() -> None: - errors = [] - HardwareLayout.load( - "tests/config/hardware-layout.json", errors=errors, raise_errors=False - ) + HardwareLayout.load("tests/config/hardware-layout.json") d = { "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py index 7726c584..1e95bad6 100644 --- a/tests/test_misc/test_flush_and_load_house.py +++ b/tests/test_misc/test_flush_and_load_house.py @@ -2,7 +2,7 @@ from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode -from gwproto.types import SpaceheatNodeGt_Maker +from gwproto.types import ElectricMeterCacGt_Maker, SpaceheatNodeGt_Maker from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker from tests.utils import flush_all @@ -12,6 +12,17 @@ def test_flush_and_load_house() -> None: load_house() successfully loads test objects""" flush_all() + unknown_electric_meter_cac_dict = { + "ComponentAttributeClassId": "c1f17330-6269-4bc5-aa4b-82e939e9b70c", + "MakeModelGtEnumSymbol": "00000000", + "DisplayName": "Unknown Power Meter", + "PollPeriodMs": 1000, + "InterfaceGtEnumSymbol": "00000000", + "TelemetryNameList": ["af39eec9"], + "TypeName": "electric.meter.cac.gt", + "Version": "000", + } + electric_meter_component_dict = { "ComponentId": "c7d352db-9a86-40f0-9601-d99243719cc5", "DisplayName": "Test unknown meter", @@ -34,6 +45,7 @@ def test_flush_and_load_house() -> None: "Version": "100", } + ElectricMeterCacGt_Maker.dict_to_dc(unknown_electric_meter_cac_dict) ElectricMeterComponentGt_Maker.dict_to_dc(electric_meter_component_dict) SpaceheatNodeGt_Maker.dict_to_dc(meter_node_dict) assert ShNode.by_id["c9456f5b-5a39-4a48-bb91-742a9fdc461d"].alias == "a.m" diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 860186c6..75841007 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -1,51 +1,91 @@ """Tests component.attribute.class.gt type, version 000""" -from typing import Any +import json -from gwproto.enums import MakeModel -from gwproto.enums.symbolized import SYMBOLIZE_ENV_VAR -from gwproto.types import ComponentAttributeClassGt -from tests.cac_load_utils import ( - CacCase, - assert_cac_load, - assert_symbolized_and_unsymbolized_encode, -) +import pytest +from pydantic import ValidationError +from gwproto.errors import SchemaError +from gwproto.types import ComponentAttributeClassGt_Maker as Maker -def test_component_attribute_class_gt_load(monkeypatch: Any) -> None: - monkeypatch.setenv(SYMBOLIZE_ENV_VAR, "1") + +def test_component_attribute_class_gt_generated() -> None: d = { "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", "DisplayName": "Sample CAC", - "MakeModelGtEnumSymbol": "00000000", "TypeName": "component.attribute.class.gt", "Version": "000", } - assert_cac_load( - [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] - ) - - -def test_encode_decode(monkeypatch: Any) -> None: - assert_symbolized_and_unsymbolized_encode( - monkeypatch=monkeypatch, - m=ComponentAttributeClassGt( - ComponentAttributeClassId="29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", - DisplayName="Sample CAC", - MakeModel=MakeModel.UNKNOWNMAKE__UNKNOWNMODEL, - ), - exp_unsymbolized={ - "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", - "DisplayName": "Sample CAC", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TypeName": "component.attribute.class.gt", - "Version": "000", - }, - exp_symbolized={ - "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", - "DisplayName": "Sample CAC", - "MakeModelGtEnumSymbol": "00000000", - "TypeName": "component.attribute.class.gt", - "Version": "000", - }, - ) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + display_name=gtuple.DisplayName, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 298e74d0..b267619b 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -1,22 +1,141 @@ """Tests electric.meter.cac.gt type, version 000""" -from gwproto.types import ElectricMeterCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_electric_meter_cac_load() -> None: +from gwproto.enums import LocalCommInterface, MakeModel +from gwproto.errors import SchemaError +from gwproto.types import ElectricMeterCacGt_Maker as Maker + + +def test_electric_meter_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - # "MakeModelGtEnumSymbol": "d300635e", - "MakeModel": "SCHNEIDERELECTRIC__IEM3455", + "MakeModelGtEnumSymbol": "d300635e", "DisplayName": "Schneider Electric Iem3455 Power Meter", - # "TelemetryNameList": ["af39eec9"], - "TelemetryNameList": ["PowerW"], + "TelemetryNameList": ["af39eec9"], "PollPeriodMs": 1000, - # "InterfaceGtEnumSymbol": "a6a4ac9f", - "Interface": "RS485", + "InterfaceGtEnumSymbol": "a6a4ac9f", "DefaultBaud": 9600, "TypeName": "electric.meter.cac.gt", "Version": "000", } - assert_cac_load([CacCase("ElectricMeterCac", d, ElectricMeterCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + display_name=gtuple.DisplayName, + telemetry_name_list=gtuple.TelemetryNameList, + poll_period_ms=gtuple.PollPeriodMs, + interface=gtuple.Interface, + default_baud=gtuple.DefaultBaud, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TelemetryNameList"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["PollPeriodMs"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["InterfaceGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + d2 = dict(d) + if "DefaultBaud" in d2: + del d2["DefaultBaud"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + d2 = dict(d, PollPeriodMs="1000.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, InterfaceGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).Interface == LocalCommInterface.default() + + d2 = dict(d, DefaultBaud="9600.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index bac4ff6a..59c1a65f 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -1,24 +1,159 @@ """Tests multipurpose.sensor.cac.gt type, version 000""" -from gwproto.types import MultipurposeSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_multipurpose_sensor_cac_gt_load() -> None: +from gwproto.enums import MakeModel, Unit +from gwproto.errors import SchemaError +from gwproto.types import MultipurposeSensorCacGt_Maker as Maker + + +def test_multipurpose_sensor_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - # "MakeModelGtEnumSymbol": "09185ae3", - "MakeModel": "GRIDWORKS__MULTITEMP1", + "MakeModelGtEnumSymbol": "09185ae3", "PollPeriodMs": 880, "Exponent": -3, - # "TempUnitGtEnumSymbol": "8e6dd6dd", - "TempUnit": "Celcius", - # "TelemetryNameList": ["22641963"], - "TelemetryNameList": ["WaterTempCTimes1000"], + "TempUnitGtEnumSymbol": "8e6dd6dd", + "TelemetryNameList": ["22641963"], "MaxThermistors": 12, "DisplayName": "Simulated GridWorks high precision water temp sensor", "CommsMethod": "I2C", "TypeName": "multipurpose.sensor.cac.gt", "Version": "000", } - assert_cac_load([CacCase("MultipurposeSensorCacGt", d, MultipurposeSensorCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + poll_period_ms=gtuple.PollPeriodMs, + exponent=gtuple.Exponent, + temp_unit=gtuple.TempUnit, + telemetry_name_list=gtuple.TelemetryNameList, + max_thermistors=gtuple.MaxThermistors, + display_name=gtuple.DisplayName, + comms_method=gtuple.CommsMethod, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["PollPeriodMs"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["Exponent"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TempUnitGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TelemetryNameList"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "MaxThermistors" in d2: + del d2["MaxThermistors"] + Maker.dict_to_tuple(d2) + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + d2 = dict(d) + if "CommsMethod" in d2: + del d2["CommsMethod"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + d2 = dict(d, PollPeriodMs="880.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, Exponent="-3.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() + + d2 = dict(d, MaxThermistors="12.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py index 2b5c3e47..43f165a5 100644 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ b/tests/types/test_pipe_flow_sensor_cac_gt.py @@ -1,17 +1,109 @@ """Tests pipe.flow.sensor.cac.gt type, version 000""" -from gwproto.types import PipeFlowSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_pipe_flow_sensor_cac_gt_load() -> None: +from gwproto.enums import MakeModel +from gwproto.errors import SchemaError +from gwproto.types import PipeFlowSensorCacGt_Maker as Maker + + +def test_pipe_flow_sensor_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - # "MakeModelGtEnumSymbol": "d0b0e375", - "MakeModel": "ATLAS__EZFLO", + "MakeModelGtEnumSymbol": "d0b0e375", "DisplayName": "EZFLO for a.tank.out", "CommsMethod": "I2C", "TypeName": "pipe.flow.sensor.cac.gt", "Version": "000", } - assert_cac_load([CacCase("PipeFlowSensorCacGt", d, PipeFlowSensorCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + display_name=gtuple.DisplayName, + comms_method=gtuple.CommsMethod, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + d2 = dict(d) + if "CommsMethod" in d2: + del d2["CommsMethod"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py index 13aa3cd1..8049a5a9 100644 --- a/tests/types/test_relay_cac_gt.py +++ b/tests/types/test_relay_cac_gt.py @@ -1,17 +1,113 @@ """Tests relay.cac.gt type, version 000""" -from gwproto.types import RelayCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_relay_cac_gt_load() -> None: +from gwproto.enums import MakeModel +from gwproto.errors import SchemaError +from gwproto.types import RelayCacGt_Maker as Maker + + +def test_relay_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - # "MakeModelGtEnumSymbol": "9cc57878", - "MakeModel": "GRIDWORKS__SIMBOOL30AMPRELAY", + "MakeModelGtEnumSymbol": "9cc57878", "DisplayName": "Gridworks Simulated Boolean Actuator", "TypicalResponseTimeMs": 400, "TypeName": "relay.cac.gt", "Version": "000", } - assert_cac_load([CacCase("RelayCacGt", d, RelayCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + display_name=gtuple.DisplayName, + typical_response_time_ms=gtuple.TypicalResponseTimeMs, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TypicalResponseTimeMs"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + d2 = dict(d, TypicalResponseTimeMs="400.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 4eae9ce0..142fb1b7 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -1,18 +1,128 @@ """Tests resistive.heater.cac.gt type, version 000""" -from gwproto.types import ResistiveHeaterCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_resistive_heater_cac_gt_load() -> None: +from gwproto.enums import MakeModel +from gwproto.errors import SchemaError +from gwproto.types import ResistiveHeaterCacGt_Maker as Maker + + +def test_resistive_heater_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - # "MakeModelGtEnumSymbol": "00000000", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "MakeModelGtEnumSymbol": "00000000", "DisplayName": "Fake Boost Element", "NameplateMaxPowerW": 4500, "RatedVoltageV": 240, "TypeName": "resistive.heater.cac.gt", "Version": "000", } - assert_cac_load([CacCase("ResistiveHeaterCacGt", d, ResistiveHeaterCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + display_name=gtuple.DisplayName, + nameplate_max_power_w=gtuple.NameplateMaxPowerW, + rated_voltage_v=gtuple.RatedVoltageV, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["NameplateMaxPowerW"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["RatedVoltageV"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + d2 = dict(d, NameplateMaxPowerW="4500.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RatedVoltageV="240.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, RatedVoltageV=0) + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py index e2294ccc..b55976fb 100644 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ b/tests/types/test_simple_temp_sensor_cac_gt.py @@ -1,23 +1,151 @@ """Tests simple.temp.sensor.cac.gt type, version 000""" -from gwproto.types import SimpleTempSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load +import json +import pytest +from pydantic import ValidationError -def test_simple_temp_sensor_cac_gt_load() -> None: +from gwproto.enums import MakeModel, TelemetryName, Unit +from gwproto.errors import SchemaError +from gwproto.types import SimpleTempSensorCacGt_Maker as Maker + + +def test_simple_temp_sensor_cac_gt_generated() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - # "MakeModelGtEnumSymbol": "acd93fb3", - "MakeModel": "ADAFRUIT__642", + "MakeModelGtEnumSymbol": "acd93fb3", "TypicalResponseTimeMs": 880, "Exponent": -3, - # "TempUnitGtEnumSymbol": "ec14bd47", - "TempUnit": "Celcius", - "TelemetryNameGtEnumSymbol": "WaterTempCTimes1000", - "TelemetryName": "", + "TempUnitGtEnumSymbol": "ec14bd47", + "TelemetryNameGtEnumSymbol": "c89d0ba1", "DisplayName": "Simulated GridWorks high precision water temp sensor", "CommsMethod": "SassyMQ", "TypeName": "simple.temp.sensor.cac.gt", "Version": "000", } - assert_cac_load([CacCase("SimpleTempSensorCacGt", d, SimpleTempSensorCacGt)]) + + with pytest.raises(SchemaError): + Maker.type_to_tuple(d) + + with pytest.raises(SchemaError): + Maker.type_to_tuple('"not a dict"') + + # Test type_to_tuple + gtype = json.dumps(d) + gtuple = Maker.type_to_tuple(gtype) + + # test type_to_tuple and tuple_to_type maps + assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple + + # test Maker init + t = Maker( + component_attribute_class_id=gtuple.ComponentAttributeClassId, + make_model=gtuple.MakeModel, + typical_response_time_ms=gtuple.TypicalResponseTimeMs, + exponent=gtuple.Exponent, + temp_unit=gtuple.TempUnit, + telemetry_name=gtuple.TelemetryName, + display_name=gtuple.DisplayName, + comms_method=gtuple.CommsMethod, + ).tuple + assert t == gtuple + + ###################################### + # Dataclass related tests + ###################################### + + dc = Maker.tuple_to_dc(gtuple) + assert gtuple == Maker.dc_to_tuple(dc) + assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc + + ###################################### + # SchemaError raised if missing a required attribute + ###################################### + + d2 = dict(d) + del d2["TypeName"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["ComponentAttributeClassId"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["MakeModelGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TypicalResponseTimeMs"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["Exponent"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TempUnitGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + d2 = dict(d) + del d2["TelemetryNameGtEnumSymbol"] + with pytest.raises(SchemaError): + Maker.dict_to_tuple(d2) + + ###################################### + # Optional attributes can be removed from type + ###################################### + + d2 = dict(d) + if "DisplayName" in d2: + del d2["DisplayName"] + Maker.dict_to_tuple(d2) + + d2 = dict(d) + if "CommsMethod" in d2: + del d2["CommsMethod"] + Maker.dict_to_tuple(d2) + + ###################################### + # Behavior on incorrect types + ###################################### + + d2 = dict(d, MakeModelGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).MakeModel == MakeModel.default() + + d2 = dict(d, TypicalResponseTimeMs="880.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, Exponent="-3.1") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + d2 = dict(d, TempUnitGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).TempUnit == Unit.default() + + d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") + assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() + + ###################################### + # SchemaError raised if TypeName is incorrect + ###################################### + + d2 = dict(d, TypeName="not the type name") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + ###################################### + # SchemaError raised if primitive attributes do not have appropriate property_format + ###################################### + + d2 = dict(d, ComponentAttributeClassId="d4be12d5-33ba-4f1f-b9e5") + with pytest.raises(ValidationError): + Maker.dict_to_tuple(d2) + + # End of Test From 50c8dbd78a97a9cb9d457460bd54fb06f3f0835e Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 10 Sep 2024 20:42:53 -0400 Subject: [PATCH 100/168] Components now have 'cac' member so they do not need the global cac dictionaries --- src/gwproto/data_classes/component.py | 14 +++++-- .../components/electric_meter_component.py | 10 ----- .../multipurpose_sensor_component.py | 10 ----- .../components/pipe_flow_sensor_component.py | 10 ----- .../components/relay_component.py | 12 +----- .../components/resistive_heater_component.py | 10 ----- .../simple_temp_sensor_component.py | 10 ----- src/gwproto/data_classes/hardware_layout.py | 9 ++++ tests/config/hardware-layout.json | 42 ++++++++++--------- tests/data_classes/test_hardware_layout.py | 9 ++++ tests/utils/__init__.py | 19 +-------- 11 files changed, 53 insertions(+), 102 deletions(-) create mode 100644 tests/data_classes/test_hardware_layout.py diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py index 94c08320..96a65a95 100644 --- a/src/gwproto/data_classes/component.py +++ b/src/gwproto/data_classes/component.py @@ -1,10 +1,10 @@ """SCADA Component Class Definition""" from abc import ABC -from typing import Dict, Optional +from typing import Any, Dict, Optional -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.data_classes.mixin import StreamlinedSerializerMixin +from gwproto.enums import MakeModel class Component(ABC, StreamlinedSerializerMixin): @@ -30,15 +30,21 @@ def __init__( component_attribute_class_id: str, display_name: Optional[str] = None, hw_uid: Optional[str] = None, + cac: Any = None, # noqa: ANN401 ) -> None: self.component_id = component_id self.display_name = display_name self.component_attribute_class_id = component_attribute_class_id self.hw_uid = hw_uid + self.cac = cac @property - def component_attribute_class(self) -> ComponentAttributeClass: - return ComponentAttributeClass.by_id[self.component_attribute_class_id] + def component_attribute_class(self) -> Any: # noqa: ANN401 + return self.cac + + @property + def make_model(self) -> MakeModel: + return self.cac.MakeModel def __repr__(self) -> str: return self.display_name diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index cf426522..e63e1cdb 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -2,9 +2,7 @@ from typing import Dict, List, Optional -from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel from gwproto.types import EgaugeIo, TelemetryReportingConfig @@ -39,13 +37,5 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 ElectricMeterComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> ElectricMeterCac: - return ElectricMeterCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 806f9117..8ada26ca 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -2,9 +2,7 @@ from typing import Dict, List, Optional -from gwproto.data_classes.cacs.multipurpose_sensor_cac import MultipurposeSensorCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel from gwproto.types import TelemetryReportingConfig @@ -31,13 +29,5 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 MultipurposeSensorComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> MultipurposeSensorCac: - return MultipurposeSensorCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 4604fe7d..6437afdc 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -2,9 +2,7 @@ from typing import Dict, Optional -from gwproto.data_classes.cacs.pipe_flow_sensor_cac import PipeFlowSensorCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel class PipeFlowSensorComponent(Component): @@ -32,13 +30,5 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 PipeFlowSensorComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> PipeFlowSensorCac: - return PipeFlowSensorCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 3d30a4e4..9f3b1798 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -2,9 +2,7 @@ from typing import Dict, Optional -from gwproto.data_classes.cacs.relay_cac import RelayCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel class RelayComponent(Component): @@ -31,13 +29,5 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 RelayComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> RelayCac: - return RelayCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.cac.MakeModel.value})" diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index fe282151..a9570904 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -2,9 +2,7 @@ from typing import Dict, Optional -from gwproto.data_classes.cacs.resistive_heater_cac import ResistiveHeaterCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel class ResistiveHeaterComponent(Component): @@ -30,13 +28,5 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 ResistiveHeaterComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> ResistiveHeaterCac: - return ResistiveHeaterCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index bcb38b6d..a4777880 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -2,9 +2,7 @@ from typing import Dict, Optional -from gwproto.data_classes.cacs.simple_temp_sensor_cac import SimpleTempSensorCac from gwproto.data_classes.component import Component -from gwproto.enums import MakeModel class SimpleTempSensorComponent(Component): @@ -28,13 +26,5 @@ def __init__( SimpleTempSensorComponent.by_id[self.component_id] = self Component.by_id[self.component_id] = self - @property - def cac(self) -> SimpleTempSensorCac: - return SimpleTempSensorCac.by_id[self.component_attribute_class_id] - - @property - def make_model(self) -> MakeModel: - return self.cac.make_model - def __repr__(self) -> str: return f"{self.display_name} ({self.cac.make_model.value})" diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 8869d860..59c8b37a 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -161,12 +161,20 @@ def load_nodes( def resolve_links( nodes: dict[str, ShNode], components: dict[str, Component], + cacs: dict[str, ComponentAttributeClassGt], *, raise_errors: bool = True, errors: Optional[list[LoadError]] = None, ) -> None: if errors is None: errors = [] + for component in components.values(): + component.cac = cacs.get(component.component_attribute_class_id, None) + if component.cac is None: + raise DataClassLoadingError( # noqa: TRY301 + f"cac {component.component_attribute_class_id} not loaded for component " + f"<{component.component_id}/<{component.display_name}>\n" + ) for node_name, node in nodes.items(): d = {"node": {"name": node_name, "node": node}} try: @@ -289,6 +297,7 @@ def load_dict( # noqa: PLR0913, PLR0917, RUF100 resolve_links( load_args["nodes"], load_args["components"], + load_args["cacs"], raise_errors=raise_errors, errors=errors, ) diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 3300426d..df7154e6 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -549,7 +549,7 @@ "ResistiveHeaterCacs": [ { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModelGtEnumSymbol": "00000000", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "DisplayName": "Fake Boost Element", "NameplateMaxPowerW": 4500, "RatedVoltageV": 240, @@ -560,7 +560,7 @@ "RelayCacs": [ { "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "MakeModelGtEnumSymbol": "e81d74a8", + "MakeModel": "GRIDWORKS__SIMBOOL30AMPRELAY", "TypicalResponseTimeMs": 400, "DisplayName": "Gridworks Simulated Relay", "TypeName": "relay.cac.gt", @@ -570,7 +570,7 @@ "PipeFlowSensorCacs": [ { "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModelGtEnumSymbol": "00000000", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "CommsMethod": "Remove this Comms Method", "DisplayName": "Some pipe flow sensor", "TypeName": "pipe.flow.sensor.cac.gt", @@ -580,11 +580,11 @@ "ElectricMeterCacs": [ { "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "MakeModelGtEnumSymbol": "076da322", + "MakeModel": "GRIDWORKS__SIMPM1", "DisplayName": "Gridworks Pm1 Simulated Power Meter", - "InterfaceGtEnumSymbol": "efc144cd", + "Interface": "SIMRABBIT", "PollPeriodMs": 1000, - "TelemetryNameList": ["af39eec9"], + "TelemetryNameList": ["PowerW"], "TypeName": "electric.meter.cac.gt", "Version": "000" } @@ -593,21 +593,21 @@ "SimpleTempSensorCacs": [ { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "MakeModelGtEnumSymbol": "f8b497e8", + "MakeModel": "GRIDWORKS__WATERTEMPHIGHPRECISION", "DisplayName": "Simulated GridWorks high precision water temp sensor", "CommsMethod": "SassyMQ", "Exponent": -3, - "TempUnitGtEnumSymbol": "7d8832f8", - "TelemetryNameGtEnumSymbol": "793505aa", + "TempUnit": "Fahrenheit", + "TelemetryName": "WaterTempFTimes1000", "TypicalResponseTimeMs": 880, "TypeName": "simple.temp.sensor.cac.gt", "Version": "000" }, { "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "793505aa", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TempUnit": "Celcius", + "TelemetryName": "WaterTempFTimes1000", "TypicalResponseTimeMs": 0, "Exponent": -3, "TypeName": "simple.temp.sensor.cac.gt", @@ -615,9 +615,9 @@ }, { "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "793505aa", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TempUnit": "Celcius", + "TelemetryName": "WaterTempFTimes1000", "TypicalResponseTimeMs": 0, "Exponent": -3, "TypeName": "simple.temp.sensor.cac.gt", @@ -627,19 +627,23 @@ "OtherCacs": [ { "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "MakeModelGtEnumSymbol": "00000000" + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt" }, { "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "MakeModelGtEnumSymbol": "00000000" + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt" }, { "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "MakeModelGtEnumSymbol": "00000000" + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt" }, { "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "MakeModelGtEnumSymbol": "00000000" + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt" } ] } diff --git a/tests/data_classes/test_hardware_layout.py b/tests/data_classes/test_hardware_layout.py new file mode 100644 index 00000000..0506b297 --- /dev/null +++ b/tests/data_classes/test_hardware_layout.py @@ -0,0 +1,9 @@ +from gwproto import HardwareLayout + + +def test_hardware_layout() -> None: + errors = [] + HardwareLayout.load( + "tests/config/hardware-layout.json", errors=errors, raise_errors=False + ) + assert not errors diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 8b08b12d..1191bec2 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1,24 +1,18 @@ from gwproto.data_classes.component import Component -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass from gwproto.data_classes.components.electric_meter_component import ( - ElectricMeterCac, ElectricMeterComponent, ) from gwproto.data_classes.components.multipurpose_sensor_component import ( - MultipurposeSensorCac, MultipurposeSensorComponent, ) from gwproto.data_classes.components.pipe_flow_sensor_component import ( - PipeFlowSensorCac, PipeFlowSensorComponent, ) -from gwproto.data_classes.components.relay_component import RelayCac, RelayComponent +from gwproto.data_classes.components.relay_component import RelayComponent from gwproto.data_classes.components.resistive_heater_component import ( - ResistiveHeaterCac, ResistiveHeaterComponent, ) from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorCac, SimpleTempSensorComponent, ) from gwproto.data_classes.sh_node import ShNode @@ -34,21 +28,10 @@ def flush_components() -> None: Component.by_id = {} -def flush_cacs() -> None: - RelayCac.by_id = {} - ElectricMeterCac.by_id = {} - MultipurposeSensorCac.by_id = {} - PipeFlowSensorCac.by_id = {} - ResistiveHeaterCac.by_id = {} - SimpleTempSensorCac.by_id = {} - ComponentAttributeClass.by_id = {} - - def flush_spaceheat_nodes() -> None: ShNode.by_id = {} def flush_all() -> None: flush_components() - flush_cacs() flush_spaceheat_nodes() From 35e4012e5333e8fc1e412a51a18d6c6c5dca5079 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Tue, 10 Sep 2024 21:13:20 -0400 Subject: [PATCH 101/168] Fix Component.make_model; poetry lock --- poetry.lock | 665 +++++++++--------- .../components/electric_meter_component.py | 2 +- .../multipurpose_sensor_component.py | 2 +- .../components/pipe_flow_sensor_component.py | 2 +- .../components/resistive_heater_component.py | 2 +- .../simple_temp_sensor_component.py | 2 +- 6 files changed, 340 insertions(+), 335 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1ccfa8d6..4d3ba8d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -331,19 +331,19 @@ files = [ [[package]] name = "filelock" -version = "3.15.4" +version = "3.16.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "flake8" @@ -559,13 +559,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.1" +version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, - {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, ] [package.dependencies] @@ -589,101 +589,103 @@ files = [ [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] [[package]] @@ -807,19 +809,19 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, + {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" @@ -881,18 +883,18 @@ files = [ [[package]] name = "pydantic" -version = "2.8.2" +version = "2.9.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, - {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, + {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, + {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, ] [package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.20.1" +annotated-types = ">=0.6.0" +pydantic-core = "2.23.3" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, @@ -900,103 +902,104 @@ typing-extensions = [ [package.extras] email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.20.1" +version = "2.23.3" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, - {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, - {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, - {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, - {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, - {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, - {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, - {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, - {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, - {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, - {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, - {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, - {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, - {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, - {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, - {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, - {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, - {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, - {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, - {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, - {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, - {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, - {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, - {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, - {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, - {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, - {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, - {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, - {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, - {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, - {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, - {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, - {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, - {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, - {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, + {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, + {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, + {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, + {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, + {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, + {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, + {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, + {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, + {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, + {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, + {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, + {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, + {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, + {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, + {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, + {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, + {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, ] [package.dependencies] @@ -1029,13 +1032,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -1157,13 +1160,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.8.0" +version = "13.8.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, - {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, ] [package.dependencies] @@ -1252,29 +1255,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.3" +version = "0.6.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3"}, - {file = "ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc"}, - {file = "ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500"}, - {file = "ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470"}, - {file = "ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f"}, - {file = "ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5"}, - {file = "ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351"}, - {file = "ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8"}, - {file = "ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521"}, - {file = "ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb"}, - {file = "ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82"}, - {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, + {file = "ruff-0.6.4-py3-none-linux_armv6l.whl", hash = "sha256:c4b153fc152af51855458e79e835fb6b933032921756cec9af7d0ba2aa01a258"}, + {file = "ruff-0.6.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bedff9e4f004dad5f7f76a9d39c4ca98af526c9b1695068198b3bda8c085ef60"}, + {file = "ruff-0.6.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d02a4127a86de23002e694d7ff19f905c51e338c72d8e09b56bfb60e1681724f"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7862f42fc1a4aca1ea3ffe8a11f67819d183a5693b228f0bb3a531f5e40336fc"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eebe4ff1967c838a1a9618a5a59a3b0a00406f8d7eefee97c70411fefc353617"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:932063a03bac394866683e15710c25b8690ccdca1cf192b9a98260332ca93408"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:50e30b437cebef547bd5c3edf9ce81343e5dd7c737cb36ccb4fe83573f3d392e"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44536df7b93a587de690e124b89bd47306fddd59398a0fb12afd6133c7b3818"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ea086601b22dc5e7693a78f3fcfc460cceabfdf3bdc36dc898792aba48fbad6"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b52387d3289ccd227b62102c24714ed75fbba0b16ecc69a923a37e3b5e0aaaa"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0308610470fcc82969082fc83c76c0d362f562e2f0cdab0586516f03a4e06ec6"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:803b96dea21795a6c9d5bfa9e96127cc9c31a1987802ca68f35e5c95aed3fc0d"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:66dbfea86b663baab8fcae56c59f190caba9398df1488164e2df53e216248baa"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:34d5efad480193c046c86608dbba2bccdc1c5fd11950fb271f8086e0c763a5d1"}, + {file = "ruff-0.6.4-py3-none-win32.whl", hash = "sha256:f0f8968feea5ce3777c0d8365653d5e91c40c31a81d95824ba61d871a11b8523"}, + {file = "ruff-0.6.4-py3-none-win_amd64.whl", hash = "sha256:549daccee5227282289390b0222d0fbee0275d1db6d514550d65420053021a58"}, + {file = "ruff-0.6.4-py3-none-win_arm64.whl", hash = "sha256:ac4b75e898ed189b3708c9ab3fc70b79a433219e1e87193b4f2b77251d058d14"}, + {file = "ruff-0.6.4.tar.gz", hash = "sha256:ac3b5bfbee99973f80aa1b7cbd1c9cbce200883bdd067300c22a6cc1c7fba212"}, ] [[package]] @@ -1346,17 +1349,17 @@ test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools [[package]] name = "sphinx-autobuild" -version = "2024.4.16" +version = "2024.9.3" description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser." optional = false python-versions = ">=3.9" files = [ - {file = "sphinx_autobuild-2024.4.16-py3-none-any.whl", hash = "sha256:f2522779d30fcbf0253e09714f274ce8c608cb6ebcd67922b1c54de59faba702"}, - {file = "sphinx_autobuild-2024.4.16.tar.gz", hash = "sha256:1c0ed37a1970eed197f9c5a66d65759e7c4e4cba7b5a5d77940752bf1a59f2c7"}, + {file = "sphinx_autobuild-2024.9.3-py3-none-any.whl", hash = "sha256:55fe9bcc05dab659650d79bed0e6beb8b6032234edbf23f028f2cac3471f0c2d"}, + {file = "sphinx_autobuild-2024.9.3.tar.gz", hash = "sha256:75929a5a92b932da8d29837406d6d973a927c456f30986a27f1f20b067897892"}, ] [package.dependencies] -colorama = "*" +colorama = ">=0.4.6" sphinx = "*" starlette = ">=0.35" uvicorn = ">=0.25" @@ -1364,7 +1367,7 @@ watchfiles = ">=0.20" websockets = ">=11" [package.extras] -test = ["pytest (>=6)"] +test = ["httpx", "pytest (>=6)"] [[package]] name = "sphinx-basic-ng" @@ -1495,13 +1498,13 @@ test = ["pytest"] [[package]] name = "starlette" -version = "0.38.2" +version = "0.38.5" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"}, - {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"}, + {file = "starlette-0.38.5-py3-none-any.whl", hash = "sha256:632f420a9d13e3ee2a6f18f437b0a9f1faecb0bc42e1942aa2ea0e379a4c4206"}, + {file = "starlette-0.38.5.tar.gz", hash = "sha256:04a92830a9b6eb1442c766199d62260c3d4dc9c4f9188360626b1e0273cb7077"}, ] [package.dependencies] @@ -1598,13 +1601,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.26.3" +version = "20.26.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, + {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, ] [package.dependencies] @@ -1839,101 +1842,103 @@ tests-strict = ["pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)"] [[package]] name = "yarl" -version = "1.9.4" +version = "1.11.1" description = "Yet another URL library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, + {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, + {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, + {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, + {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, + {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, + {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, + {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, + {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, + {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, + {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, + {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, + {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, + {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, + {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, ] [package.dependencies] diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index e63e1cdb..7202a04a 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -38,4 +38,4 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 Component.by_id[self.component_id] = self def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.make_model.value})" diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 8ada26ca..072e7074 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -30,4 +30,4 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 Component.by_id[self.component_id] = self def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.make_model.value})" diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 6437afdc..8bbfcc5c 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -31,4 +31,4 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 Component.by_id[self.component_id] = self def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.make_model.value})" diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index a9570904..51b7954d 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -29,4 +29,4 @@ def __init__( # noqa: PLR0913, PLR0917, RUF100 Component.by_id[self.component_id] = self def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.make_model.value})" diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index a4777880..0420e355 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -27,4 +27,4 @@ def __init__( Component.by_id[self.component_id] = self def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.make_model.value})" + return f"{self.display_name} ({self.make_model.value})" From 7b65c8bf37fb819c76e996ab69566eb8eacbbb93 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 11 Sep 2024 17:50:48 -0400 Subject: [PATCH 102/168] Updated components: - gwproto.types no longer accesses gwproto.data_classes; instead HardwareLayout. - gwproto.types components inherit from ComponentGt. - gwproto.types components no longer have _Maker classes. - gwproto.types components no longer have enum symbolization accesses data classes based off the name of the gwproto.types class. - Component data classes Have-A ComponentGt and a ComponentAttributeClassGt. - ShNode Is-A SpaceheatNodeGt. - Removed comments / doc from field definition, for short term visibility. - Existing test code reduced to taking the existing dictionary and verifying calling load_components() on it. --- pyproject.toml | 4 +- src/gwproto/__init__.py | 2 - src/gwproto/data_classes/component.py | 50 -- .../data_classes/components/__init__.py | 43 ++ .../data_classes/components/component.py | 21 + .../components/electric_meter_component.py | 43 +- .../fibaro_smart_implant_component.py | 22 +- .../components/hubitat_component.py | 26 +- .../components/hubitat_poller_component.py | 57 +- .../components/hubitat_tank_component.py | 65 +-- .../multipurpose_sensor_component.py | 35 +- .../components/pipe_flow_sensor_component.py | 35 +- .../components/relay_component.py | 32 +- .../components/resistive_heater_component.py | 33 +- .../components/rest_poller_component.py | 27 +- .../simple_temp_sensor_component.py | 31 +- .../components/web_server_component.py | 28 +- src/gwproto/data_classes/hardware_layout.py | 384 ++++++------- src/gwproto/data_classes/resolver.py | 2 +- src/gwproto/data_classes/sh_node.py | 62 +- src/gwproto/default_decoders.py | 76 +-- src/gwproto/property_format.py | 24 +- src/gwproto/types/__init__.py | 38 +- .../types/component_attribute_class_gt.py | 2 +- src/gwproto/types/component_gt.py | 281 +-------- src/gwproto/types/components.py | 31 + src/gwproto/types/egauge_io.py | 154 +---- src/gwproto/types/egauge_register_config.py | 154 +---- .../types/electric_meter_component_gt.py | 417 +------------- .../fibaro_smart_implant_component_gt.py | 79 +-- src/gwproto/types/hubitat_cac_gt.py | 2 +- src/gwproto/types/hubitat_component_gt.py | 48 -- src/gwproto/types/hubitat_poller_cac_gt.py | 2 +- .../types/hubitat_poller_component_gt.py | 36 -- src/gwproto/types/hubitat_tank_cac_gt.py | 2 +- .../types/hubitat_tank_component_gt.py | 79 +-- .../types/multipurpose_sensor_component_gt.py | 348 +----------- .../types/pipe_flow_sensor_component_gt.py | 309 +--------- src/gwproto/types/relay_component_gt.py | 302 +--------- .../types/resistive_heater_component_gt.py | 297 +--------- src/gwproto/types/rest_poller_component_gt.py | 74 +-- .../types/simple_temp_sensor_component_gt.py | 288 +--------- src/gwproto/types/spaceheat_node_gt.py | 412 +------------- .../types/telemetry_reporting_config.py | 288 +--------- src/gwproto/types/web_server_cac_gt.py | 2 +- src/gwproto/types/web_server_component_gt.py | 29 - src/gwproto/utils.py | 18 - tests/cac_load_utils.py | 7 +- tests/component_load_utils.py | 138 +++++ tests/config/hardware-layout.json | 195 ++++--- tests/conftest.py | 10 - .../test_electric_meter_component.py | 56 -- tests/data_classes/test_hardware_layout.py | 6 +- tests/test_misc/__init__.py | 0 tests/test_misc/test_flush_and_load_house.py | 535 ------------------ tests/types/test_component_gt.py | 98 +--- tests/types/test_egauge_io.py | 64 +-- tests/types/test_egauge_register_config.py | 92 +-- .../types/test_electric_meter_component_gt.py | 179 +----- .../types/test_multipurpose_sensor_cac_gt.py | 3 - .../test_multipurpose_sensor_component_gt.py | 135 +---- .../test_pipe_flow_sensor_component_gt.py | 137 +---- tests/types/test_relay_component_gt.py | 118 +--- .../test_resistive_heater_component_gt.py | 125 +--- .../test_simple_temp_sensor_component_gt.py | 117 +--- tests/types/test_spaceheat_node_gt.py | 171 +----- .../types/test_telemetry_reporting_config.py | 148 +---- tests/utils/__init__.py | 37 -- 68 files changed, 894 insertions(+), 6271 deletions(-) delete mode 100644 src/gwproto/data_classes/component.py create mode 100644 src/gwproto/data_classes/components/component.py create mode 100644 src/gwproto/types/components.py create mode 100644 tests/component_load_utils.py delete mode 100644 tests/data_classes/test_electric_meter_component.py delete mode 100644 tests/test_misc/__init__.py delete mode 100644 tests/test_misc/test_flush_and_load_house.py delete mode 100644 tests/utils/__init__.py diff --git a/pyproject.toml b/pyproject.toml index b3988b07..c1a9c75f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ source = ["gwproto", "tests"] [tool.coverage.report] show_missing = false -fail_under = 85 +fail_under = 80 [tool.black] extend_exclude = "src/gwproto/gt/|src/gwproto/gs/|src/gwproto/enums/" @@ -146,6 +146,8 @@ ignore = [ "RUF100", # ruff and IDE often disagree about whether a 'noqa' is in use. "W191", "W291", # Formatter + "W293", # Formatter + "W391", # Formatter ] diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index 3aaa1fef..afcab9ec 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -20,7 +20,6 @@ from gwproto.default_decoders import ( CacDecoder, ComponentDecoder, - decode_to_data_class, default_cac_decoder, default_component_decoder, ) @@ -52,7 +51,6 @@ "as_enum", "create_discriminator", "create_message_payload_discriminator", - "decode_to_data_class", "default_cac_decoder", "default_component_decoder", "get_pydantic_literal_type_name", diff --git a/src/gwproto/data_classes/component.py b/src/gwproto/data_classes/component.py deleted file mode 100644 index 96a65a95..00000000 --- a/src/gwproto/data_classes/component.py +++ /dev/null @@ -1,50 +0,0 @@ -"""SCADA Component Class Definition""" - -from abc import ABC -from typing import Any, Dict, Optional - -from gwproto.data_classes.mixin import StreamlinedSerializerMixin -from gwproto.enums import MakeModel - - -class Component(ABC, StreamlinedSerializerMixin): - by_id: Dict[str, "Component"] = {} # noqa: RUF012 - base_props = [ # noqa: RUF012 - "component_id", - "display_name", - "component_attribute_class_id", - "hw_uid", - ] - - def __new__(cls, component_id, *args, **kwargs) -> "Component": # noqa: ANN001, ANN002, ANN003, ARG003 - try: - return cls.by_id[component_id] - except KeyError: - instance = super().__new__(cls) - cls.by_id[component_id] = instance - return instance - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - cac: Any = None, # noqa: ANN401 - ) -> None: - self.component_id = component_id - self.display_name = display_name - self.component_attribute_class_id = component_attribute_class_id - self.hw_uid = hw_uid - self.cac = cac - - @property - def component_attribute_class(self) -> Any: # noqa: ANN401 - return self.cac - - @property - def make_model(self) -> MakeModel: - return self.cac.MakeModel - - def __repr__(self) -> str: - return self.display_name diff --git a/src/gwproto/data_classes/components/__init__.py b/src/gwproto/data_classes/components/__init__.py index e69de29b..90708092 100644 --- a/src/gwproto/data_classes/components/__init__.py +++ b/src/gwproto/data_classes/components/__init__.py @@ -0,0 +1,43 @@ +from gwproto.data_classes.components.component import Component +from gwproto.data_classes.components.electric_meter_component import ( + ElectricMeterComponent, +) +from gwproto.data_classes.components.fibaro_smart_implant_component import ( + FibaroSmartImplantComponent, +) +from gwproto.data_classes.components.hubitat_component import HubitatComponent +from gwproto.data_classes.components.hubitat_poller_component import ( + HubitatPollerComponent, +) +from gwproto.data_classes.components.hubitat_tank_component import HubitatTankComponent +from gwproto.data_classes.components.multipurpose_sensor_component import ( + MultipurposeSensorComponent, +) +from gwproto.data_classes.components.pipe_flow_sensor_component import ( + PipeFlowSensorComponent, +) +from gwproto.data_classes.components.relay_component import RelayComponent +from gwproto.data_classes.components.resistive_heater_component import ( + ResistiveHeaterComponent, +) +from gwproto.data_classes.components.rest_poller_component import RESTPollerComponent +from gwproto.data_classes.components.simple_temp_sensor_component import ( + SimpleTempSensorComponent, +) +from gwproto.data_classes.components.web_server_component import WebServerComponent + +__all__ = [ + "Component", + "ElectricMeterComponent", + "FibaroSmartImplantComponent", + "HubitatComponent", + "HubitatPollerComponent", + "HubitatTankComponent", + "MultipurposeSensorComponent", + "PipeFlowSensorComponent", + "RESTPollerComponent", + "RelayComponent", + "ResistiveHeaterComponent", + "SimpleTempSensorComponent", + "WebServerComponent", +] diff --git a/src/gwproto/data_classes/components/component.py b/src/gwproto/data_classes/components/component.py new file mode 100644 index 00000000..4b03ac2e --- /dev/null +++ b/src/gwproto/data_classes/components/component.py @@ -0,0 +1,21 @@ +"""SCADA Component Class Definition""" + +from typing import Generic, TypeVar + +from pydantic import BaseModel + +from gwproto.types import ComponentAttributeClassGt, ComponentGt + +ComponentT = TypeVar("ComponentT", bound=ComponentGt) +CacT = TypeVar("CacT", bound=ComponentAttributeClassGt) + + +class Component(BaseModel, Generic[ComponentT, CacT]): + gt: ComponentT + cac: CacT + + def __repr__(self) -> str: + return f"<{self.gt.DisplayName}> ({self.cac.MakeModel.value})" + + +class ComponentOnly(Component[ComponentGt, Component]): ... diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index 7202a04a..fc0eb897 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -1,41 +1,10 @@ """ElectricMeterComponent definition""" -from typing import Dict, List, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import ElectricMeterCacGt +from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt -from gwproto.data_classes.component import Component -from gwproto.types import EgaugeIo, TelemetryReportingConfig - -class ElectricMeterComponent(Component): - by_id: Dict[str, "ElectricMeterComponent"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - modbus_host: Optional[str] = None, - modbus_port: Optional[int] = None, - config_list: Optional[List[TelemetryReportingConfig]] = None, - egauge_io_list: Optional[List[EgaugeIo]] = None, - ) -> None: - if egauge_io_list is None: - egauge_io_list = [] - if config_list is None: - config_list = [] - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.modbus_host = modbus_host - self.modbus_port = modbus_port - self.config_list = config_list - self.egauge_io_list = egauge_io_list - ElectricMeterComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.make_model.value})" +class ElectricMeterComponent( + Component[ElectricMeterComponentGt, ElectricMeterCacGt] +): ... diff --git a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py index 62b135fa..3a91e7ea 100644 --- a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py +++ b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py @@ -1,19 +1,7 @@ -from typing import Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import FibaroSmartImplantCacGt, FibaroSmartImplantComponentGt -from gwproto.data_classes.component import Component - -class FibaroSmartImplantComponent(Component): - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - super().__init__( - component_id=component_id, - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - hw_uid=hw_uid, - ) +class FibaroSmartImplantComponent( + Component[FibaroSmartImplantComponentGt, FibaroSmartImplantCacGt] +): ... diff --git a/src/gwproto/data_classes/components/hubitat_component.py b/src/gwproto/data_classes/components/hubitat_component.py index 14e46917..9ba84d4d 100644 --- a/src/gwproto/data_classes/components/hubitat_component.py +++ b/src/gwproto/data_classes/components/hubitat_component.py @@ -2,33 +2,19 @@ import yarl -from gwproto.data_classes.component import Component -from gwproto.types.hubitat_gt import HubitatGt +from gwproto.data_classes.components.component import Component +from gwproto.types import HubitatCacGt, HubitatComponentGt -class HubitatComponent(Component): - hubitat_gt: HubitatGt +class HubitatComponent(Component[HubitatComponentGt, HubitatCacGt]): web_listener_nodes: set[str] - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - hubitat_gt: HubitatGt, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - self.hubitat_gt = hubitat_gt + def __init__(self, gt: HubitatComponentGt, cac: HubitatCacGt) -> None: + super().__init__(gt, cac) self.web_listener_nodes = set() - super().__init__( - component_id=component_id, - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - hw_uid=hw_uid, - ) def urls(self) -> dict[str, Optional[yarl.URL]]: - return self.hubitat_gt.urls() + return self.gt.urls() def add_web_listener(self, web_listener_node: str) -> None: self.web_listener_nodes.add(web_listener_node) diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index 31c43f64..3ea281f8 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -2,36 +2,25 @@ import yarl -from gwproto.data_classes.component import Component +from gwproto.data_classes.components import HubitatComponent +from gwproto.data_classes.components.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode +from gwproto.types import HubitatPollerCacGt, HubitatPollerComponentGt from gwproto.types.hubitat_component_gt import HubitatComponentGt -from gwproto.types.hubitat_poller_gt import HubitatPollerGt from gwproto.types.rest_poller_gt import RequestArgs, RESTPollerSettings from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig -class HubitatPollerComponent(Component, ComponentResolver): +class HubitatPollerComponent( + Component[HubitatPollerComponentGt, HubitatPollerCacGt], ComponentResolver +): hubitat_gt: HubitatComponentGt - poller_gt: HubitatPollerGt _rest: Optional[RESTPollerSettings] = None - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - poller_gt: HubitatPollerGt, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - self.hubitat_gt = HubitatComponentGt.make_stub(poller_gt.hubitat_component_id) - self.poller_gt = poller_gt - super().__init__( - component_id=component_id, - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - hw_uid=hw_uid, - ) + def __init__(self, gt: HubitatPollerComponentGt, cac: HubitatPollerCacGt) -> None: + super().__init__(gt, cac) + self.hubitat_gt = HubitatComponentGt.make_stub(gt.Poller.hubitat_component_id) @property def rest(self) -> RESTPollerSettings: @@ -55,32 +44,36 @@ def resolve( # replace proxy hubitat component, which only had component id. # with the actual hubitat component containing data. - hubitat_component = HubitatComponentGt.from_component_id( - self.hubitat_gt.ComponentId, - components, - ) - self.hubitat_gt = HubitatComponentGt.from_data_class(hubitat_component) + hubitat_component = components.get(self.hubitat_gt.ComponentId, None) + if hubitat_component is None or not isinstance( + hubitat_component, HubitatComponent + ): + raise ValueError( + f"ERROR. Component for {self.hubitat_gt.ComponentId} " + f"has type <{type(hubitat_component)}>. Expected " + ) + self.hubitat_gt = hubitat_component.gt # Constuct url config on top of maker api url url config self._rest = RESTPollerSettings( request=RequestArgs( - url=self.hubitat_gt.refresh_url_config(self.poller_gt.device_id) + url=self.hubitat_gt.refresh_url_config(self.gt.Poller.device_id) ), - poll_period_seconds=self.poller_gt.poll_period_seconds, + poll_period_seconds=self.gt.Poller.poll_period_seconds, ) # register attributes which accept web posts if ( - self.poller_gt.web_listen_enabled - and hubitat_component.hubitat_gt.WebListenEnabled + self.gt.Poller.web_listen_enabled + and hubitat_component.gt.Hubitat.WebListenEnabled ): - for attribute in self.poller_gt.attributes: + for attribute in self.gt.Poller.attributes: if attribute.web_listen_enabled: hubitat_component.add_web_listener(node_name) def urls(self) -> dict[str, Optional[yarl.URL]]: urls = self.hubitat_gt.urls() - for attribute in self.poller_gt.attributes: + for attribute in self.gt.Poller.attributes: urls[attribute.node_name] = self.rest.url return urls @@ -95,5 +88,5 @@ def config_list(self) -> list[TelemetryReportingConfig]: Exponent=attribute.exponent, Unit=attribute.unit, ) - for attribute in self.poller_gt.attributes + for attribute in self.gt.Poller.attributes ] diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index cacc7700..40d32186 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -2,9 +2,11 @@ import yarl -from gwproto.data_classes.component import Component +from gwproto.data_classes.components import HubitatComponent +from gwproto.data_classes.components.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode +from gwproto.types import HubitatTankCacGt, HubitatTankComponentGt from gwproto.types.hubitat_component_gt import ( HubitatComponentGt, HubitatRESTResolutionSettings, @@ -12,43 +14,38 @@ from gwproto.types.hubitat_tank_gt import ( FibaroTempSensorSettings, FibaroTempSensorSettingsGt, - HubitatTankSettingsGt, ) from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig -class HubitatTankComponent(Component, ComponentResolver): +class HubitatTankComponent( + Component[HubitatTankComponentGt, HubitatTankCacGt], ComponentResolver +): hubitat: HubitatComponentGt - sensor_supply_voltage: float - default_poll_period_seconds: Optional[float] = None devices_gt: list[FibaroTempSensorSettingsGt] devices: list[FibaroTempSensorSettings] - web_listen_enabled: bool - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - tank_gt: HubitatTankSettingsGt, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: + def __init__(self, gt: HubitatTankComponentGt, cac: HubitatTankCacGt) -> None: + super().__init__(gt, cac) # Create self.hubitat as a proxy containing only the id # of the hubitat; the actual component data will be resolved # when resolve() is called; Here in the constructor we cannot # rely on the actual HubitatComponentGt existing yet. - self.hubitat = HubitatComponentGt.make_stub(tank_gt.hubitat_component_id) - self.sensor_supply_voltage = tank_gt.sensor_supply_voltage - self.default_poll_period_seconds = tank_gt.default_poll_period_seconds - self.devices_gt = list(tank_gt.devices) - self.web_listen_enabled = tank_gt.web_listen_enabled + self.hubitat = HubitatComponentGt.make_stub(self.gt.Tank.hubitat_component_id) + self.devices_gt = list(self.gt.Tank.devices) self.devices = [] - super().__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) + + @property + def sensor_supply_voltage(self) -> float: + return self.gt.Tank.sensor_supply_voltage + + @property + def default_poll_period_seconds(self) -> Optional[float]: + return self.gt.Tank.default_poll_period_seconds + + @property + def web_listen_enabled(self) -> bool: + return self.gt.Tank.web_listen_enabled def resolve( self, @@ -56,11 +53,15 @@ def resolve( nodes: dict[str, ShNode], components: dict[str, Component], ) -> None: - hubitat_component = HubitatComponentGt.from_component_id( - self.hubitat.ComponentId, components - ) - hubitat_component_gt = HubitatComponentGt.from_data_class(hubitat_component) - hubitat_settings = HubitatRESTResolutionSettings(hubitat_component_gt) + hubitat_component = components.get(self.hubitat.ComponentId, None) + if hubitat_component is None or not isinstance( + hubitat_component, HubitatComponent + ): + raise ValueError( + f"ERROR. Component for {self.hubitat.ComponentId} " + f"has type <{type(hubitat_component)}>. Expected " + ) + hubitat_settings = HubitatRESTResolutionSettings(hubitat_component.gt) devices = [ FibaroTempSensorSettings.create( tank_name=tank_node_name, @@ -78,11 +79,11 @@ def resolve( ) # replace proxy hubitat component, which only had component id. # with the actual hubitat component containing data. - self.hubitat = hubitat_component_gt + self.hubitat = hubitat_component.gt self.devices = devices # register voltage attribute for fibaros which accept web posts - if self.web_listen_enabled and hubitat_component.hubitat_gt.WebListenEnabled: + if self.web_listen_enabled and hubitat_component.gt.Hubitat.WebListenEnabled: for device in self.devices: if device.web_listen_enabled: hubitat_component.add_web_listener(tank_node_name) diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 072e7074..2738e4c9 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -1,33 +1,10 @@ """MutlipurposeSensorComponent definition""" -from typing import Dict, List, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import MultipurposeSensorCacGt +from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt -from gwproto.data_classes.component import Component -from gwproto.types import TelemetryReportingConfig - -class MultipurposeSensorComponent(Component): - by_id: Dict[str, "MultipurposeSensorComponent"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - channel_list: List[int], - config_list: List[TelemetryReportingConfig], - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.channel_list = channel_list - self.config_list = config_list - MultipurposeSensorComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.make_model.value})" +class MultipurposeSensorComponent( + Component[MultipurposeSensorComponentGt, MultipurposeSensorCacGt] +): ... diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py index 8bbfcc5c..8b660b58 100644 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py @@ -1,34 +1,9 @@ """PipeFlowSensorComponent definition""" -from typing import Dict, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import PipeFlowSensorCacGt, PipeFlowSensorComponentGt -from gwproto.data_classes.component import Component - -class PipeFlowSensorComponent(Component): - by_id: Dict[str, "PipeFlowSensorComponent"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - conversion_factor: float, - i2c_address: int, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - expected_max_gpm_times100: Optional[int] = None, - ) -> None: - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.i2c_address = i2c_address - self.conversion_factor = conversion_factor - self.expected_max_gpm_times100 = expected_max_gpm_times100 - PipeFlowSensorComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.make_model.value})" +class PipeFlowSensorComponent( + Component[PipeFlowSensorComponentGt, PipeFlowSensorCacGt] +): ... diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py index 9f3b1798..f88b03e8 100644 --- a/src/gwproto/data_classes/components/relay_component.py +++ b/src/gwproto/data_classes/components/relay_component.py @@ -1,33 +1,7 @@ """RelayComponent definition""" -from typing import Dict, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import RelayCacGt, RelayComponentGt -from gwproto.data_classes.component import Component - -class RelayComponent(Component): - by_id: Dict[str, "RelayComponent"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - normally_open: bool, # noqa: FBT001 - display_name: Optional[str] = None, - gpio: Optional[int] = None, - hw_uid: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.normally_open = normally_open - self.gpio = gpio - - RelayComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.cac.MakeModel.value})" +class RelayComponent(Component[RelayComponentGt, RelayCacGt]): ... diff --git a/src/gwproto/data_classes/components/resistive_heater_component.py b/src/gwproto/data_classes/components/resistive_heater_component.py index 51b7954d..1345ac21 100644 --- a/src/gwproto/data_classes/components/resistive_heater_component.py +++ b/src/gwproto/data_classes/components/resistive_heater_component.py @@ -1,32 +1,9 @@ """ResistiveHeaterComponent definition""" -from typing import Dict, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import ResistiveHeaterCacGt, ResistiveHeaterComponentGt -from gwproto.data_classes.component import Component - -class ResistiveHeaterComponent(Component): - by_id: Dict[str, "ResistiveHeaterComponent"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - tested_max_hot_milli_ohms: Optional[int] = None, - tested_max_cold_milli_ohms: Optional[int] = None, - hw_uid: Optional[str] = None, - display_name: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.tested_max_hot_milli_ohms = tested_max_hot_milli_ohms - self.tested_max_cold_milli_ohms = tested_max_cold_milli_ohms - ResistiveHeaterComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.make_model.value})" +class ResistiveHeaterComponent( + Component[ResistiveHeaterComponentGt, ResistiveHeaterCacGt] +): ... diff --git a/src/gwproto/data_classes/components/rest_poller_component.py b/src/gwproto/data_classes/components/rest_poller_component.py index 8caa79b1..76ffcca7 100644 --- a/src/gwproto/data_classes/components/rest_poller_component.py +++ b/src/gwproto/data_classes/components/rest_poller_component.py @@ -1,24 +1,9 @@ -from typing import Optional - -from gwproto.data_classes.component import Component +from gwproto.data_classes.components.component import Component +from gwproto.types import RESTPollerCacGt, RESTPollerComponentGt from gwproto.types.rest_poller_gt import RESTPollerSettings -class RESTPollerComponent(Component): - rest: RESTPollerSettings - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - rest: RESTPollerSettings, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - self.rest = rest - super().__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) +class RESTPollerComponent(Component[RESTPollerComponentGt, RESTPollerCacGt]): + @property + def rest(self) -> RESTPollerSettings: + return self.gt.Rest diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py index 0420e355..85379302 100644 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ b/src/gwproto/data_classes/components/simple_temp_sensor_component.py @@ -1,30 +1,9 @@ """SimpleTempSensorComponent definition""" -from typing import Dict, Optional +from gwproto.data_classes.components.component import Component +from gwproto.types import SimpleTempSensorCacGt, SimpleTempSensorComponentGt -from gwproto.data_classes.component import Component - -class SimpleTempSensorComponent(Component): - by_id: Dict[str, "SimpleTempSensorComponent"] = {} # noqa: RUF012 - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - channel: Optional[int] = None, - ) -> None: - super(self.__class__, self).__init__( - display_name=display_name, - component_id=component_id, - hw_uid=hw_uid, - component_attribute_class_id=component_attribute_class_id, - ) - self.channel: Optional[int] = channel - SimpleTempSensorComponent.by_id[self.component_id] = self - Component.by_id[self.component_id] = self - - def __repr__(self) -> str: - return f"{self.display_name} ({self.make_model.value})" +class SimpleTempSensorComponent( + Component[SimpleTempSensorComponentGt, SimpleTempSensorCacGt] +): ... diff --git a/src/gwproto/data_classes/components/web_server_component.py b/src/gwproto/data_classes/components/web_server_component.py index a228eaf8..f733d029 100644 --- a/src/gwproto/data_classes/components/web_server_component.py +++ b/src/gwproto/data_classes/components/web_server_component.py @@ -1,24 +1,10 @@ -from typing import Optional - -from gwproto.data_classes.component import Component +from gwproto.data_classes.components.component import Component +from gwproto.types.web_server_cac_gt import WebServerCacGt +from gwproto.types.web_server_component_gt import WebServerComponentGt from gwproto.types.web_server_gt import WebServerGt -class WebServerComponent(Component): - web_server_gt: WebServerGt - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - web_server_gt: WebServerGt, - display_name: Optional[str] = None, - hw_uid: Optional[str] = None, - ) -> None: - self.web_server_gt = web_server_gt - super().__init__( - component_id=component_id, - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - hw_uid=hw_uid, - ) +class WebServerComponent(Component[WebServerComponentGt, WebServerCacGt]): + @property + def web_server_gt(self) -> WebServerGt: + return self.gt.WebServer diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 59c8b37a..17c0022f 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -7,7 +7,6 @@ import copy import json -import re import typing from collections import defaultdict from dataclasses import dataclass @@ -15,8 +14,10 @@ from pathlib import Path from typing import Any, List, Optional, Type, TypeVar +import gwproto.data_classes.components from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac -from gwproto.data_classes.component import Component +from gwproto.data_classes.components import Component +from gwproto.data_classes.components.component import ComponentOnly from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) @@ -33,25 +34,11 @@ from gwproto.enums import ActorClass, Role, TelemetryName from gwproto.types import ( ComponentAttributeClassGt, - PipeFlowSensorComponentGt_Maker, - RelayComponentGt_Maker, - ResistiveHeaterComponentGt_Maker, - SimpleTempSensorComponentGt_Maker, - SpaceheatNodeGt_Maker, -) -from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker -from gwproto.types.multipurpose_sensor_component_gt import ( - MultipurposeSensorComponentGt_Maker, + ComponentGt, ) T = TypeVar("T") -snake_add_underscore_to_camel_pattern = re.compile(r"(? str: - return snake_add_underscore_to_camel_pattern.sub("_", name).lower() - @dataclass class LoadError: @@ -60,142 +47,6 @@ class LoadError: exception: Exception -def load_cacs( - layout: dict[str, Any], - *, - raise_errors: bool = True, - errors: Optional[list[LoadError]] = None, - cac_decoder: Optional[CacDecoder] = None, -) -> dict[str, ComponentAttributeClassGt]: - if errors is None: - errors = [] - if cac_decoder is None: - cac_decoder = default_cac_decoder - cacs = {} - for type_name in [ - "RelayCacs", - "ResistiveHeaterCacs", - "ElectricMeterCacs", - "PipeFlowSensorCacs", - "MultipurposeSensorCacs", - "SimpleTempSensorCacs", - "OtherCacs", - ]: - for cac_dict in layout.get(type_name, ()): - try: - cac = cac_decoder.decode(cac_dict) - cacs[cac.ComponentAttributeClassId] = cac - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError(type_name, cac_dict, e)) - return cacs - - -def load_components( # noqa: C901 - layout: dict[Any, Any], - *, - raise_errors: bool = True, - errors: Optional[list[LoadError]] = None, - component_decoder: Optional[ComponentDecoder] = None, -) -> dict[Any, Any]: - if errors is None: - errors = [] - components = {} - for type_name, maker_class in [ - ("RelayComponents", RelayComponentGt_Maker), - ("ResistiveHeaterComponents", ResistiveHeaterComponentGt_Maker), - ("ElectricMeterComponents", ElectricMeterComponentGt_Maker), - ("PipeFlowSensorComponents", PipeFlowSensorComponentGt_Maker), - ("MultipurposeSensorComponents", MultipurposeSensorComponentGt_Maker), - ("SimpleTempSensorComponents", SimpleTempSensorComponentGt_Maker), - ]: - for d in layout.get(type_name, []): - try: - components[d["ComponentId"]] = maker_class.dict_to_dc( # type:ignore[attr-defined] - d - ) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError(type_name, d, e)) - if component_decoder is None: - component_decoder = default_component_decoder - for d in layout["OtherComponents"]: - component_type = d.get("TypeName", "") - try: - if component_type and component_type in component_decoder: - component = component_decoder.decode_to_data_class(d) - else: - component = Component(**{camel_to_snake(k): v for k, v in d.items()}) - components[d["ComponentId"]] = component - except Exception as e: - if raise_errors: - raise - errors.append(LoadError("OtherComponents", d, e)) - return components - - -def load_nodes( - layout: dict[Any, Any], - *, - raise_errors: bool = True, - errors: Optional[list[LoadError]] = None, - included_node_names: Optional[set[str]] = None, -) -> dict[Any, Any]: - nodes = {} - if errors is None: - errors = [] - for d in layout.get("ShNodes", []): - try: - node_name = d["Alias"] - if included_node_names is None or node_name in included_node_names: - nodes[node_name] = SpaceheatNodeGt_Maker.dict_to_dc(d) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError("ShNode", d, e)) - return nodes - - -def resolve_links( - nodes: dict[str, ShNode], - components: dict[str, Component], - cacs: dict[str, ComponentAttributeClassGt], - *, - raise_errors: bool = True, - errors: Optional[list[LoadError]] = None, -) -> None: - if errors is None: - errors = [] - for component in components.values(): - component.cac = cacs.get(component.component_attribute_class_id, None) - if component.cac is None: - raise DataClassLoadingError( # noqa: TRY301 - f"cac {component.component_attribute_class_id} not loaded for component " - f"<{component.component_id}/<{component.display_name}>\n" - ) - for node_name, node in nodes.items(): - d = {"node": {"name": node_name, "node": node}} - try: - if node.component_id is not None: - component = components.get(node.component_id, None) - if component is None: - raise DataClassLoadingError( # noqa: TRY301 - f"{node.alias} component {node.component_id} not loaded!" - ) - if isinstance(component, ComponentResolver): - component.resolve( - node.alias, - nodes, - components, - ) - except Exception as e: - if raise_errors: - raise - errors.append(LoadError("ShNode", d, e)) - - class HardwareLayout: layout: dict[Any, Any] cacs: dict[str, ComponentAttributeClassGt] @@ -204,28 +55,178 @@ class HardwareLayout: nodes: dict[str, ShNode] nodes_by_component: dict[str, str] + GT_SUFFIX = "Gt" + + @classmethod + def load_cacs( + cls, + layout: dict[str, Any], + *, + raise_errors: bool = True, + errors: Optional[list[LoadError]] = None, + cac_decoder: Optional[CacDecoder] = None, + ) -> dict[str, ComponentAttributeClassGt]: + if errors is None: + errors = [] + if cac_decoder is None: + cac_decoder = default_cac_decoder + cacs: dict[str, ComponentAttributeClassGt] = {} + for type_name in [ + "RelayCacs", + "ResistiveHeaterCacs", + "ElectricMeterCacs", + "PipeFlowSensorCacs", + "MultipurposeSensorCacs", + "SimpleTempSensorCacs", + "OtherCacs", + ]: + for cac_dict in layout.get(type_name, ()): + try: + cac = cac_decoder.decode(cac_dict) + cacs[cac.ComponentAttributeClassId] = cac + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError(type_name, cac_dict, e)) + return cacs + + @classmethod + def get_data_class_name(cls, component_gt: ComponentGt) -> str: + gt_class_name = component_gt.__class__.__name__ + if not gt_class_name.endswith(cls.GT_SUFFIX) or len(gt_class_name) <= len( + cls.GT_SUFFIX + ): + raise DataClassLoadingError( # noqa: TRY301 + f"Name of decoded component class ({gt_class_name}) " + f"must end with <{cls.GT_SUFFIX}> " + f"and be longer than {len(cls.GT_SUFFIX)} chars" + ) + return gt_class_name[: -len(cls.GT_SUFFIX)] + + @classmethod + def get_data_class_class(cls, component_gt: ComponentGt) -> Type[Component]: + return getattr( + gwproto.data_classes.components, + cls.get_data_class_name(component_gt), + ComponentOnly, + ) + + @classmethod + def make_component( + cls, component_gt: ComponentGt, cac: ComponentAttributeClassGt + ) -> Component: + if cac is None: + raise DataClassLoadingError( # noqa: TRY301 + f"cac {component_gt.ComponentAttributeClassId} not loaded for component " + f"<{component_gt.ComponentId}/<{component_gt.DisplayName}>\n" + ) + return cls.get_data_class_class(component_gt)(gt=component_gt, cac=cac) + + @classmethod + def load_components( + cls, + layout: dict[Any, Any], + cacs: dict[str, ComponentAttributeClassGt], + *, + raise_errors: bool = True, + errors: Optional[list[LoadError]] = None, + component_decoder: Optional[ComponentDecoder] = None, + ) -> dict[str, Component]: + if errors is None: + errors = [] + if component_decoder is None: + component_decoder = default_component_decoder + components = {} + for type_name in [ + "RelayComponents", + "ResistiveHeaterComponents", + "ElectricMeterComponents", + "PipeFlowSensorComponents", + "MultipurposeSensorComponents", + "SimpleTempSensorComponents", + "OtherComponents", + ]: + for component_dict in layout.get(type_name, ()): + try: + component_gt = component_decoder.decode(component_dict) + components[component_gt.ComponentId] = cls.make_component( + component_gt, + cacs.get(component_gt.ComponentAttributeClassId, None), + ) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError(type_name, component_dict, e)) + return components + + @classmethod + def load_nodes( + cls, + layout: dict[Any, Any], + *, + raise_errors: bool = True, + errors: Optional[list[LoadError]] = None, + included_node_names: Optional[set[str]] = None, + ) -> dict[str, ShNode]: + nodes = {} + if errors is None: + errors = [] + for node_dict in layout.get("ShNodes", []): + try: + node_name = node_dict["Alias"] + if included_node_names is None or node_name in included_node_names: + nodes[node_name] = ShNode.model_validate(node_dict) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("ShNode", node_dict, e)) + return nodes + + @classmethod + def resolve_links( + cls, + nodes: dict[str, ShNode], + components: dict[str, Component], + *, + raise_errors: bool = True, + errors: Optional[list[LoadError]] = None, + ) -> None: + if errors is None: + errors = [] + for node_name, node in nodes.items(): + d = {"node": {"name": node_name, "node": node}} + try: + if node.component_id is not None: + component = components.get(node.component_id, None) + if component is None: + raise DataClassLoadingError( # noqa: TRY301 + f"{node.alias} component {node.component_id} not loaded!" + ) + if isinstance(component, ComponentResolver): + component.resolve( + node.alias, + nodes, + components, + ) + except Exception as e: + if raise_errors: + raise + errors.append(LoadError("ShNode", d, e)) + def __init__( self, layout: dict[Any, Any], *, - cacs: Optional[dict[str, ComponentAttributeClassGt]] = None, - components: Optional[dict[str, Component]] = None, - nodes: Optional[dict[str, ShNode]] = None, + cacs: dict[str, ComponentAttributeClassGt], + components: dict[str, Component], + nodes: dict[str, ShNode], ) -> None: self.layout = copy.deepcopy(layout) - if cacs is None: - self.cacs = {} - else: - self.cacs = dict(cacs) - if components is None: - self.components = {} - else: - self.components = dict(components) + self.cacs = dict(cacs) + self.components = dict(components) self.components_by_type = defaultdict(list) for component in self.components.values(): self.components_by_type[type(component)].append(component) - if nodes is None: - nodes = ShNode.by_id self.nodes = dict(nodes) self.nodes_by_component = { node.component_id: node.alias for node in self.nodes.values() @@ -240,12 +241,12 @@ def clear_property_cache(self) -> None: self.__dict__.pop(cached_prop_name, None) @classmethod - def load( # noqa: PLR0913, PLR0917, RUF100 + def load( # noqa: PLR0913 cls, layout_path: Path | str, *, included_node_names: Optional[set[str]] = None, - raise_errors: bool = True, # noqa: FBT001, FBT002 + raise_errors: bool = True, errors: Optional[list[LoadError]] = None, cac_decoder: Optional[CacDecoder] = None, component_decoder: Optional[ComponentDecoder] = None, @@ -262,7 +263,7 @@ def load( # noqa: PLR0913, PLR0917, RUF100 ) @classmethod - def load_dict( # noqa: PLR0913, PLR0917, RUF100 + def load_dict( # noqa: PLR0913 cls, layout: dict[Any, Any], *, @@ -274,30 +275,31 @@ def load_dict( # noqa: PLR0913, PLR0917, RUF100 ) -> "HardwareLayout": if errors is None: errors = [] + cacs = cls.load_cacs( + layout=layout, + raise_errors=raise_errors, + errors=errors, + cac_decoder=cac_decoder, + ) load_args = { - "cacs": load_cacs( - layout=layout, - raise_errors=raise_errors, - errors=errors, - cac_decoder=cac_decoder, - ), - "components": load_components( + "cacs": cacs, + "components": cls.load_components( layout=layout, + cacs=cacs, raise_errors=raise_errors, errors=errors, component_decoder=component_decoder, ), - "nodes": load_nodes( + "nodes": cls.load_nodes( layout=layout, raise_errors=raise_errors, errors=errors, included_node_names=included_node_names, ), } - resolve_links( + cls.resolve_links( load_args["nodes"], load_args["components"], - load_args["cacs"], raise_errors=raise_errors, errors=errors, ) @@ -310,7 +312,7 @@ def component(self, node_alias: str) -> Optional[Component]: return self.component_from_node(self.node(node_alias, None)) def cac(self, node_alias: str) -> Optional[ComponentAttributeClassGt]: - return self.cac_from_component(self.component(node_alias)) + return self.component(node_alias).cac def get_component_as_type(self, component_id: str, type_: Type[T]) -> Optional[T]: component = self.components.get(component_id, None) @@ -343,18 +345,6 @@ def component_from_node(self, node: Optional[ShNode]) -> Optional[Component]: else None ) - def cac_from_component( - self, component: Optional[Component] - ) -> Optional[ComponentAttributeClassGt]: - return ( - self.cacs.get( - component.component_attribute_class_id if component is not None else "", - None, - ) - if component is not None - else None - ) - @classmethod def parent_alias(cls, alias: str) -> str: last_delimiter = alias.rfind(".") @@ -432,7 +422,7 @@ def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: SensorNode=self.power_meter_node, TelemetryName=config.TelemetryName, ) - for config in self.power_meter_component.config_list + for config in self.power_meter_component.gt.ConfigList ] @cached_property @@ -450,12 +440,10 @@ def power_meter_component(self) -> ElectricMeterComponent: @cached_property def power_meter_cac(self) -> ElectricMeterCac: - if not isinstance( - self.power_meter_component.component_attribute_class, ElectricMeterCac - ): + if not isinstance(self.power_meter_component.cac, ElectricMeterCac): raise TypeError( - f"ERROR. power_meter_component cac {self.power_meter_component.component_attribute_class}" - f" / {type(self.power_meter_component.component_attribute_class)} is not an ElectricMeterCac" + f"ERROR. power_meter_component cac {self.power_meter_component.cac}" + f" / {type(self.power_meter_component.cac)} is not an ElectricMeterCac" ) return self.power_meter_node.component.component_attribute_class # type: ignore[union-attr, return-value] diff --git a/src/gwproto/data_classes/resolver.py b/src/gwproto/data_classes/resolver.py index eb01a8e7..ec294035 100644 --- a/src/gwproto/data_classes/resolver.py +++ b/src/gwproto/data_classes/resolver.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import NoReturn -from gwproto.data_classes.component import Component +from gwproto.data_classes.components.component import Component from gwproto.data_classes.sh_node import ShNode diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index ac0094a3..89362b46 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -1,13 +1,14 @@ """ShNode definition""" -from typing import Dict, Optional +from typing import Optional -from gwproto.data_classes.component import Component +from gwproto.data_classes.components.component import Component from gwproto.data_classes.errors import DataClassLoadingError from gwproto.enums import ActorClass, Role +from gwproto.types import SpaceheatNodeGt -class ShNode: +class ShNode(SpaceheatNodeGt): """ A SpaceheatNode, or ShNode, is an organizing principal for the SCADA software. ShNodes can represent both underlying physical objects (water tank), measurements of these @@ -16,32 +17,37 @@ class ShNode: temperature data for the purposes of thermostatic control). """ - by_id: Dict[str, "ShNode"] = {} # noqa: RUF012 + @property + def sh_node_id(self) -> str: + return self.ShNodeId + + @property + def alias(self) -> str: + return self.Alias + + @property + def actor_class(self) -> ActorClass: + return self.ActorClass + + @property + def role(self) -> Role: + return self.Role - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - sh_node_id: str, - alias: str, - actor_class: ActorClass, - role: Role, - display_name: Optional[str] = None, - component_id: Optional[str] = None, - reporting_sample_period_s: Optional[int] = None, - rated_voltage_v: Optional[int] = None, - typical_voltage_v: Optional[int] = None, - in_power_metering: Optional[bool] = None, - ) -> None: - self.sh_node_id = sh_node_id - self.alias = alias - self.actor_class = actor_class - self.role = role - self.display_name = display_name - self.component_id = component_id - self.reporting_sample_period_s = reporting_sample_period_s - self.rated_voltage_v = rated_voltage_v - self.typical_voltage_v = typical_voltage_v - self.in_power_metering = in_power_metering - ShNode.by_id[self.sh_node_id] = self + @property + def display_name(self) -> Optional[str]: + return self.DisplayName + + @property + def component_id(self) -> Optional[str]: + return self.ComponentId + + @property + def reporting_sample_period_s(self) -> Optional[int]: + return self.ReportingSamplePeriodS + + @property + def in_power_metering(self) -> Optional[bool]: + return self.InPowerMetering def __repr__(self) -> str: rs = f"ShNode {self.display_name} => {self.role.value} {self.alias}, " diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index 8f79d550..8333c82a 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -1,47 +1,17 @@ -# ruff: noqa: ANN401, RUF100 - +# ruff: noqa: ANN401, RUF100, import re -import typing -from typing import Any, Type, TypeVar +from typing import Any, TypeVar from pydantic import ValidationError import gwproto.types.cacs -import gwproto.types.fibaro_smart_implant_component_gt # noqa: F401 -import gwproto.types.hubitat_component_gt # noqa: F401 -import gwproto.types.hubitat_poller_component_gt # noqa: F401 -import gwproto.types.hubitat_tank_component_gt # noqa: F401 -import gwproto.types.rest_poller_component_gt # noqa: F401 -import gwproto.types.web_server_component_gt # noqa: F401 -from gwproto.data_classes.component import Component +import gwproto.types.components from gwproto.decoders import PydanticTypeNameDecoder -from gwproto.types import ComponentAttributeClassGt +from gwproto.types import ComponentAttributeClassGt, ComponentGt T = TypeVar("T") -def decode_to_data_class( - decoded_gt: typing.Any, - return_type: Type[T], - *, - allow_missing_func: bool = True, - allow_non_instance: bool = False, -) -> T: - if hasattr(decoded_gt, "to_data_class"): - data_class = decoded_gt.to_data_class() - if not allow_non_instance and not isinstance(data_class, return_type): - raise ValueError( - f"ERROR. Returned data class {type(data_class)} is not" - f" instance of {return_type}" - ) - return typing.cast(T, decoded_gt.to_data_class()) - if allow_missing_func: - raise ValueError( - f"ERROR. Decoded type {type(decoded_gt)} has no" f" to_data_class() method." - ) - return Component(**decoded_gt.dict()) - - class CacDecoder(PydanticTypeNameDecoder): TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") @@ -71,24 +41,29 @@ def decode( class ComponentDecoder(PydanticTypeNameDecoder): - TYPE_NAME_REGEX = re.compile(r".*\.component\.gt") + TYPE_NAME_REGEX = re.compile(r".*\.?component\.gt") def __init__(self, model_name: str, **kwargs: Any) -> None: if "type_name_regex" not in kwargs: kwargs["type_name_regex"] = ComponentDecoder.TYPE_NAME_REGEX super().__init__(model_name, **kwargs) - def decode_to_data_class( - self, - data: dict, - *, - allow_missing_func: bool = True, - ) -> Component: - return decode_to_data_class( - decoded_gt=self.decode_obj(data), - return_type=Component, - allow_missing_func=allow_missing_func, - ) + def decode(self, d: dict, *, allow_missing: bool = True) -> ComponentGt: + try: + decoded = self.loader.model_validate({self.payload_field_name: d}).Payload + if not isinstance(decoded, ComponentGt): + raise TypeError( + f"ERROR. ComponentDecoder decoded type {type(decoded)}, " + "not ComponentGt" + ) + except ValidationError as e: + if allow_missing and any( + error.get("type") == "union_tag_invalid" for error in e.errors() + ): + decoded = ComponentGt(**d) + else: + raise + return decoded default_cac_decoder = CacDecoder( @@ -98,12 +73,5 @@ def decode_to_data_class( default_component_decoder = ComponentDecoder( model_name="DefaultComponentDecoder", - module_names=[ - "gwproto.types.fibaro_smart_implant_component_gt", - "gwproto.types.hubitat_component_gt", - "gwproto.types.hubitat_poller_component_gt", - "gwproto.types.hubitat_tank_component_gt", - "gwproto.types.rest_poller_component_gt", - "gwproto.types.web_server_component_gt", - ], + modules=[gwproto.types.components], ) diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 929fc34f..f482959d 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -2,10 +2,12 @@ import string import struct +import uuid from datetime import datetime, timezone -from typing import Any, Callable, List +from typing import Annotated, Any, Callable, List import pydantic +from pydantic import BeforeValidator def predicate_validator( @@ -149,7 +151,7 @@ def is_left_right_dot(candidate: str) -> bool: return candidate.islower() -def check_is_left_right_dot(candidate: str) -> None: +def check_is_left_right_dot(candidate: str) -> str: """Lowercase AlphanumericStrings separated by dots (i.e. periods), with most significant word to the left. I.e. `d1.ne` is the child of `d1`. Checking the format cannot verify the significance of words. All @@ -177,6 +179,24 @@ def check_is_left_right_dot(candidate: str) -> None: ) if not candidate.islower(): raise ValueError(f"alias must be lowercase. Got '{candidate}'") + return candidate + + +LeftRightDotStr = Annotated[str, BeforeValidator(check_is_left_right_dot)] + + +def str_is_valid_uuid4(v: str) -> str: + v = str(v) + try: + u = uuid.UUID(v) + except Exception as e: + raise ValueError(f"Invalid UUID4: {v} <{e}>") from e + if u.version != 4: + raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") + return str(u) + + +UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] def is_lru_alias_format(candidate: str) -> bool: diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index d370029b..e2978e29 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -1,14 +1,11 @@ """List of all the types""" -from gwproto.types.component_attribute_class_gt import ( - ComponentAttributeClassGt, -) -from gwproto.types.component_gt import ComponentGt, ComponentGt_Maker +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types.component_gt import ComponentGt from gwproto.types.data_channel import DataChannel, DataChannel_Maker -from gwproto.types.egauge_io import EgaugeIo, EgaugeIo_Maker +from gwproto.types.egauge_io import EgaugeIo from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, - EgaugeRegisterConfig_Maker, ) from gwproto.types.electric_meter_cac_gt import ( ElectricMeterCacGt, @@ -18,7 +15,6 @@ ) from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, - FibaroSmartImplantComponentGt_Maker, ) from gwproto.types.gt_dispatch_boolean import GtDispatchBoolean, GtDispatchBoolean_Maker from gwproto.types.gt_dispatch_boolean_local import ( @@ -58,12 +54,10 @@ ) from gwproto.types.hubitat_poller_component_gt import ( HubitatPollerComponentGt, - HubitatPollerComponentGt_Maker, ) from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt from gwproto.types.hubitat_tank_component_gt import ( HubitatTankComponentGt, - HubitatTankComponentGt_Maker, ) from gwproto.types.multipurpose_sensor_cac_gt import ( MultipurposeSensorCacGt, @@ -73,36 +67,31 @@ ) from gwproto.types.pipe_flow_sensor_component_gt import ( PipeFlowSensorComponentGt, - PipeFlowSensorComponentGt_Maker, ) from gwproto.types.power_watts import PowerWatts, PowerWatts_Maker from gwproto.types.relay_cac_gt import RelayCacGt -from gwproto.types.relay_component_gt import RelayComponentGt, RelayComponentGt_Maker +from gwproto.types.relay_component_gt import RelayComponentGt from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, ) from gwproto.types.resistive_heater_component_gt import ( ResistiveHeaterComponentGt, - ResistiveHeaterComponentGt_Maker, ) from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt from gwproto.types.rest_poller_component_gt import ( RESTPollerComponentGt, - RESTPollerComponentGt_Maker, ) from gwproto.types.simple_temp_sensor_cac_gt import ( SimpleTempSensorCacGt, ) from gwproto.types.simple_temp_sensor_component_gt import ( SimpleTempSensorComponentGt, - SimpleTempSensorComponentGt_Maker, ) from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat, SnapshotSpaceheat_Maker -from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt, SpaceheatNodeGt_Maker +from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt from gwproto.types.ta_data_channels import TaDataChannels, TaDataChannels_Maker from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, - TelemetryReportingConfig_Maker, ) from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, @@ -113,19 +102,14 @@ __all__ = [ "ComponentAttributeClassGt", "ComponentGt", - "ComponentGt_Maker", "DataChannel", "DataChannel_Maker", "EgaugeIo", - "EgaugeIo_Maker", "EgaugeRegisterConfig", - "EgaugeRegisterConfig_Maker", "ElectricMeterCacGt", # "ElectricMeterComponentGt", - # "ElectricMeterComponentGt_Maker", "FibaroSmartImplantCacGt", "FibaroSmartImplantComponentGt", - "FibaroSmartImplantComponentGt_Maker", "GtDispatchBoolean", "GtDispatchBooleanLocal", "GtDispatchBooleanLocal_Maker", @@ -152,39 +136,31 @@ "HubitatComponentGt", "HubitatPollerCacGt", "HubitatPollerComponentGt", - "HubitatPollerComponentGt_Maker", "HubitatTankCacGt", "HubitatTankComponentGt", - "HubitatTankComponentGt_Maker", "MultipurposeSensorCacGt", # "MultipurposeSensorComponentGt", - # "MultipurposeSensorComponentGt_Maker", "PipeFlowSensorCacGt", "PipeFlowSensorComponentGt", - "PipeFlowSensorComponentGt_Maker", "PowerWatts", "PowerWatts_Maker", "RESTPollerCacGt", "RESTPollerComponentGt", - "RESTPollerComponentGt_Maker", "RelayCacGt", "RelayComponentGt", - "RelayComponentGt_Maker", "ResistiveHeaterCacGt", "ResistiveHeaterComponentGt", - "ResistiveHeaterComponentGt_Maker", "SimpleTempSensorCacGt", "SimpleTempSensorComponentGt", - "SimpleTempSensorComponentGt_Maker", "SnapshotSpaceheat", "SnapshotSpaceheat_Maker", "SpaceheatNodeGt", - "SpaceheatNodeGt_Maker", "TaDataChannels", "TaDataChannels_Maker", "TelemetryReportingConfig", - "TelemetryReportingConfig_Maker", "TelemetrySnapshotSpaceheat", "TelemetrySnapshotSpaceheat_Maker", "WebServerGt", + "cacs", # noqa: F822 + "components", # noqa: F822 ] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index dd96a865..b4fae19a 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from gwproto.enums import MakeModel -from gwproto.utils import UUID4Str +from gwproto.property_format import UUID4Str class ComponentAttributeClassGt(BaseModel): diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index e0c27041..7eb60519 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -1,283 +1,16 @@ """Type component.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.data_classes.component import Component -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import UUID4Str class ComponentGt(BaseModel): - """ - Component Gt. - - Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, - DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks - 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable - it is meant to be an immutable identifier associated to a specific - physical device, ideally one that can be read remotely by the SCADA and also by the naked - eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component.html) - """ - - ComponentId: str = Field( - title="Component Id", - description="Primary identifier for components in all GridWorks registries.", - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description=( - "This is an optional, mutable field whose use is strongly encouraged. It may include " - "information about HOW the component is used in a hardware layout. It may also include " - "the HwUid for the component." - ), - default=None, - ) - HwUid: Optional[str] = Field( - title="HwUid", - description="Usually this is determined by the inheriting class.", - default=None, - ) + ComponentId: UUID4Str + ComponentAttributeClassId: UUID4Str + DisplayName: Optional[str] = None + HwUid: Optional[str] = None TypeName: Literal["component.gt"] = "component.gt" Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"}, - by_alias=True, - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the component.gt.000 representation. - - Instances in the class are python-native representations of component.gt.000 - objects, while the actual component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ComponentGt_Maker: - type_name = "component.gt" - version = "000" - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str], - hw_uid: Optional[str], - ) -> None: - self.tuple = ComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - HwUid=hw_uid, - ) - - @classmethod - def tuple_to_type(cls, tuple_: ComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tuple_.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ComponentGt: - """ - Deserialize a dictionary representation of a component.gt.000 message object - into a ComponentGt python object for internal use. - - This is the near-inverse of the ComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ComponentGt object. - - Returns: - ComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ComponentGt) -> Component: - if t.ComponentId in Component.by_id: - dc = Component.by_id[t.ComponentId] - else: - dc = Component( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - hw_uid=t.HwUid, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: Component) -> ComponentGt: - return ComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - hw_uid=dc.hw_uid, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> Component: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: Component) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> Component: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/components.py b/src/gwproto/types/components.py new file mode 100644 index 00000000..3f038332 --- /dev/null +++ b/src/gwproto/types/components.py @@ -0,0 +1,31 @@ +from gwproto.types.component_gt import ComponentGt +from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt +from gwproto.types.fibaro_smart_implant_component_gt import ( + FibaroSmartImplantComponentGt, +) +from gwproto.types.hubitat_component_gt import HubitatComponentGt +from gwproto.types.hubitat_poller_component_gt import HubitatPollerComponentGt +from gwproto.types.hubitat_tank_component_gt import HubitatTankComponentGt +from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt +from gwproto.types.pipe_flow_sensor_component_gt import PipeFlowSensorComponentGt +from gwproto.types.relay_component_gt import RelayComponentGt +from gwproto.types.resistive_heater_component_gt import ResistiveHeaterComponentGt +from gwproto.types.rest_poller_component_gt import RESTPollerComponentGt +from gwproto.types.simple_temp_sensor_component_gt import SimpleTempSensorComponentGt +from gwproto.types.web_server_component_gt import WebServerComponentGt + +__all__ = [ + "ComponentGt", + "ElectricMeterComponentGt", + "FibaroSmartImplantComponentGt", + "HubitatComponentGt", + "HubitatPollerComponentGt", + "HubitatTankComponentGt", + "MultipurposeSensorComponentGt", + "PipeFlowSensorComponentGt", + "RESTPollerComponentGt", + "RelayComponentGt", + "ResistiveHeaterComponentGt", + "SimpleTempSensorComponentGt", + "WebServerComponentGt", +] diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index ea2ec4eb..2b1d1976 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -1,27 +1,16 @@ """Type egauge.io, version 000""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal from pydantic import BaseModel, Field -from gwproto.errors import SchemaError from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, - EgaugeRegisterConfig_Maker, ) from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, - TelemetryReportingConfig_Maker, ) -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - class EgaugeIo(BaseModel): """ @@ -51,144 +40,3 @@ class EgaugeIo(BaseModel): ) TypeName: Literal["egauge.io"] = "egauge.io" Version: Literal["000"] = "000" - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - egauge.io.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - egauge.io.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - d["InputConfig"] = self.InputConfig.as_dict() - d["OutputConfig"] = self.OutputConfig.as_dict() - return d - - def as_type(self) -> bytes: - """ - Serialize to the egauge.io.000 representation. - - Instances in the class are python-native representations of egauge.io.000 - objects, while the actual egauge.io.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is EgaugeIo.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class EgaugeIo_Maker: - type_name = "egauge.io" - version = "000" - - def __init__( - self, - input_config: EgaugeRegisterConfig, - output_config: TelemetryReportingConfig, - ) -> None: - self.tuple = EgaugeIo( - InputConfig=input_config, - OutputConfig=output_config, - ) - - @classmethod - def tuple_to_type(cls, tpl: EgaugeIo) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> EgaugeIo: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeIo: - """ - Deserialize a dictionary representation of a egauge.io.000 message object - into a EgaugeIo python object for internal use. - - This is the near-inverse of the EgaugeIo.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a EgaugeIo object. - - Returns: - EgaugeIo - """ - d2 = dict(d) - if "InputConfig" not in d2: - raise SchemaError(f"dict missing InputConfig: <{d2}>") - if not isinstance(d2["InputConfig"], dict): - raise SchemaError( - f"InputConfig <{d2['InputConfig']}> must be a EgaugeRegisterConfig!" - ) - input_config = EgaugeRegisterConfig_Maker.dict_to_tuple(d2["InputConfig"]) - d2["InputConfig"] = input_config - if "OutputConfig" not in d2: - raise SchemaError(f"dict missing OutputConfig: <{d2}>") - if not isinstance(d2["OutputConfig"], dict): - raise SchemaError( - f"OutputConfig <{d2['OutputConfig']}> must be a TelemetryReportingConfig!" - ) - output_config = TelemetryReportingConfig_Maker.dict_to_tuple(d2["OutputConfig"]) - d2["OutputConfig"] = output_config - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret egauge.io version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return EgaugeIo(**d2) diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index 4c55e186..eb711266 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -1,19 +1,9 @@ """Type egauge.register.config, version 000""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - class EgaugeRegisterConfig(BaseModel): """ @@ -64,145 +54,3 @@ class EgaugeRegisterConfig(BaseModel): ) TypeName: Literal["egauge.register.config"] = "egauge.register.config" Version: Literal["000"] = "000" - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - egauge.register.config.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - egauge.register.config.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the egauge.register.config.000 representation. - - Instances in the class are python-native representations of egauge.register.config.000 - objects, while the actual egauge.register.config.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is EgaugeRegisterConfig.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class EgaugeRegisterConfig_Maker: - type_name = "egauge.register.config" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - address: int, - name: str, - description: str, - type: str, # noqa - denominator: int, - unit: str, - ) -> None: - self.tuple = EgaugeRegisterConfig( - Address=address, - Name=name, - Description=description, - Type=type, - Denominator=denominator, - Unit=unit, - ) - - @classmethod - def tuple_to_type(cls, tpl: EgaugeRegisterConfig) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> EgaugeRegisterConfig: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> EgaugeRegisterConfig: - """ - Deserialize a dictionary representation of a egauge.register.config.000 message object - into a EgaugeRegisterConfig python object for internal use. - - This is the near-inverse of the EgaugeRegisterConfig.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a EgaugeRegisterConfig object. - - Returns: - EgaugeRegisterConfig - """ - d2 = dict(d) - if "Address" not in d2: - raise SchemaError(f"dict missing Address: <{d2}>") - if "Name" not in d2: - raise SchemaError(f"dict missing Name: <{d2}>") - if "Description" not in d2: - raise SchemaError(f"dict missing Description: <{d2}>") - if "Type" not in d2: - raise SchemaError(f"dict missing Type: <{d2}>") - if "Denominator" not in d2: - raise SchemaError(f"dict missing Denominator: <{d2}>") - if "Unit" not in d2: - raise SchemaError(f"dict missing Unit: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret egauge.register.config version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return EgaugeRegisterConfig(**d2) diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index bf8a6558..5749ac51 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -1,86 +1,22 @@ """Type electric.meter.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, List, Literal, Optional, Self +from typing import Literal, Optional, Self -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import Field, PositiveInt, model_validator -from gwproto.data_classes.components.electric_meter_component import ( - ElectricMeterComponent, -) -from gwproto.errors import SchemaError -from gwproto.types.egauge_io import EgaugeIo, EgaugeIo_Maker +from gwproto.types import ComponentGt +from gwproto.types.egauge_io import EgaugeIo from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, - TelemetryReportingConfig_Maker, -) - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" ) -LOGGER = logging.getLogger(__name__) - - -class ElectricMeterComponentGt(BaseModel): - """ - Type for tracking Electric Meter Components. - Designed for specific instances of Electric Meters. It extends the component.gt.000 type. - Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, - DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks - 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable - it is meant to be an immutable identifier associated to a specific - physical device, ideally one that can be read remotely by the SCADA and also by the naked - eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry. - [More info](https://g-node-registry.readthedocs.io/en/latest/electric-meters.html) - """ - - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of an ElectricMeter, " - "and also as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - DisplayName: Optional[str] = Field( - title="Display Name for the Power Meter", - description="Sample: Oak EGauge6074", - default=None, - ) - ConfigList: List[TelemetryReportingConfig] = Field( - title="List of Data Channel configs ", - description=( - "This power meter will produce multiple data channels. Each data channel measures " - "a certain quantities (like power, current) for certain ShNodes (like a boost element " - "or heat pump)." - ), - ) - HwUid: Optional[str] = Field( - title="Unique Hardware Id for the Power Meter", - description="For eGauge, use what comes back over modbus address 100.", - default=None, - ) - ModbusHost: Optional[str] = Field( - title="Host on LAN when power meter is modbus over Ethernet", - default=None, - ) - ModbusPort: Optional[int] = Field( - title="ModbusPort", - default=None, - ) - EgaugeIoList: List[EgaugeIo] = Field( +class ElectricMeterComponentGt(ComponentGt): + ConfigList: list[TelemetryReportingConfig] + ModbusHost: Optional[str] = None + ModbusPort: Optional[PositiveInt] = None + EgaugeIoList: list[EgaugeIo] = Field( + default=[], title="Bijecton from EGauge4030 input to ConfigList output", description=( "This should be empty unless the MakeModel of the corresponding component attribute " @@ -93,61 +29,12 @@ class ElectricMeterComponentGt(BaseModel): TypeName: Literal["electric.meter.component.gt"] = "electric.meter.component.gt" Version: Literal["000"] = "000" - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ModbusPort") - @classmethod - def _check_modbus_port(cls, v: Optional[int]) -> Optional[int]: - if v is None: - return v - try: - check_is_non_negative_integer(v) - except ValueError as e: - raise ValueError( - f"ModbusPort failed NonNegativeInteger format validation: {e}" - ) - return v - @model_validator(mode="after") - def check_axiom_1(self) -> Self: - """ - Axiom 1: Modbus consistency. - ModbusHost requires a ModbusPort - """ + def check_axioms(self) -> Self: if self.ModbusHost is not None and self.ModbusPort is None: raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") - return self - - @model_validator(mode="after") - def check_axiom_2(self) -> Self: - """ - Axiom 2: Egauge4030 consistency. - If the EgaugeIoList has non-zero length, then the ModbusHost is not None and - the set of output configs is equal to ConfigList as a set - """ if len(self.EgaugeIoList) == 0: return self - if self.ModbusHost is None: raise ValueError( "Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist!" @@ -158,285 +45,3 @@ def check_axiom_2(self) -> Self: "output configs must equal ConfigList as a set" ) return self - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - electric.meter.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - electric.meter.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - # Recursively calling as_dict() - d["ConfigList"] = [elt.as_dict() for elt in self.ConfigList] - # Recursively calling as_dict() - d["EgaugeIoList"] = [elt.as_dict() for elt in self.EgaugeIoList] - return d - - def as_type(self) -> bytes: - """ - Serialize to the electric.meter.component.gt.000 representation. - - Instances in the class are python-native representations of electric.meter.component.gt.000 - objects, while the actual electric.meter.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ElectricMeterComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ElectricMeterComponentGt_Maker: - type_name = "electric.meter.component.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str], - config_list: List[TelemetryReportingConfig], - hw_uid: Optional[str], - modbus_host: Optional[str], - modbus_port: Optional[int], - egauge_io_list: List[EgaugeIo], - ) -> None: - self.tuple = ElectricMeterComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - ConfigList=config_list, - HwUid=hw_uid, - ModbusHost=modbus_host, - ModbusPort=modbus_port, - EgaugeIoList=egauge_io_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: ElectricMeterComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ElectricMeterComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ElectricMeterComponentGt: # noqa: C901, PLR0912 - """ - Deserialize a dictionary representation of a electric.meter.component.gt.000 message object - into a ElectricMeterComponentGt python object for internal use. - - This is the near-inverse of the ElectricMeterComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ElectricMeterComponentGt object. - - Returns: - ElectricMeterComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "ConfigList" not in d2: - raise SchemaError(f"dict missing ConfigList: <{d2}>") - if not isinstance(d2["ConfigList"], List): - raise SchemaError(f"ConfigList <{d2['ConfigList']}> must be a List!") - config_list = [] - for elt in d2["ConfigList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"ConfigList <{d2['ConfigList']}> must be a List of TelemetryReportingConfig types" - ) - t = TelemetryReportingConfig_Maker.dict_to_tuple(elt) - config_list.append(t) - d2["ConfigList"] = config_list - if "EgaugeIoList" not in d2: - raise SchemaError(f"dict missing EgaugeIoList: <{d2}>") - if not isinstance(d2["EgaugeIoList"], List): - raise SchemaError(f"EgaugeIoList <{d2['EgaugeIoList']}> must be a List!") - egauge_io_list = [] - for elt in d2["EgaugeIoList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"EgaugeIoList <{d2['EgaugeIoList']}> must be a List of EgaugeIo types" - ) - t = EgaugeIo_Maker.dict_to_tuple(elt) - egauge_io_list.append(t) - d2["EgaugeIoList"] = egauge_io_list - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret electric.meter.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ElectricMeterComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ElectricMeterComponentGt) -> ElectricMeterComponent: - if t.ComponentId in ElectricMeterComponent.by_id: - dc = ElectricMeterComponent.by_id[t.ComponentId] - else: - dc = ElectricMeterComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - config_list=t.ConfigList, - hw_uid=t.HwUid, - modbus_host=t.ModbusHost, - modbus_port=t.ModbusPort, - egauge_io_list=t.EgaugeIoList, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ElectricMeterComponent) -> ElectricMeterComponentGt: - return ElectricMeterComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - config_list=dc.config_list, - hw_uid=dc.hw_uid, - modbus_host=dc.modbus_host, - modbus_port=dc.modbus_port, - egauge_io_list=dc.egauge_io_list, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ElectricMeterComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) - - @classmethod - def dc_to_type(cls, dc: ElectricMeterComponent) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ElectricMeterComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_non_negative_integer(v: int) -> None: - """ - Must be non-negative when interpreted as an integer. Interpretation - as an integer follows the pydantic rules for this - which will round - down rational numbers. So 0 is fine, and 1.7 will be interpreted as - 1 and is also fine. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 0 - """ - v2 = int(v) - if v2 < 0: - raise ValueError(f"<{v}> is not NonNegativeInteger") - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/fibaro_smart_implant_component_gt.py b/src/gwproto/types/fibaro_smart_implant_component_gt.py index f3c5aaff..686b3e57 100644 --- a/src/gwproto/types/fibaro_smart_implant_component_gt.py +++ b/src/gwproto/types/fibaro_smart_implant_component_gt.py @@ -1,11 +1,5 @@ -import json -import typing -from typing import Any, Literal +from typing import Literal -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.fibaro_smart_implant_component import ( - FibaroSmartImplantComponent, -) from gwproto.types import ComponentGt @@ -14,74 +8,3 @@ class FibaroSmartImplantComponentGt(ComponentGt): TypeName: Literal["fibaro.smart.implant.component.gt"] = ( "fibaro.smart.implant.component.gt" ) - Version: Literal["000"] = "000" - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - @classmethod - def from_data_class( - cls, component: FibaroSmartImplantComponent - ) -> "FibaroSmartImplantComponentGt": - return FibaroSmartImplantComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - DisplayName=component.display_name, - HwUid=component.hw_uid, - ) - - def to_data_class(self) -> FibaroSmartImplantComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(FibaroSmartImplantComponent, component) - return FibaroSmartImplantComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) - - -class FibaroSmartImplantComponentGt_Maker: - type_name: str = FibaroSmartImplantComponentGt.model_fields["TypeName"].default - version = "000" - tuple: FibaroSmartImplantComponentGt - - def __init__(self, component: FibaroSmartImplantComponent) -> None: - self.tuple = FibaroSmartImplantComponentGt.from_data_class(component) - - @classmethod - def tuple_to_type(cls, tpl: FibaroSmartImplantComponentGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> FibaroSmartImplantComponentGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> FibaroSmartImplantComponentGt: - return FibaroSmartImplantComponentGt(**d) - - @classmethod - def tuple_to_dc( - cls, t: FibaroSmartImplantComponentGt - ) -> FibaroSmartImplantComponent: - return t.to_data_class() - - @classmethod - def dc_to_tuple( - cls, dc: FibaroSmartImplantComponent - ) -> FibaroSmartImplantComponentGt: - return FibaroSmartImplantComponentGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> FibaroSmartImplantComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: FibaroSmartImplantComponent) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> FibaroSmartImplantComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py index b79d643a..9056dfb7 100644 --- a/src/gwproto/types/hubitat_cac_gt.py +++ b/src/gwproto/types/hubitat_cac_gt.py @@ -1,6 +1,6 @@ from typing import Literal -from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types import ComponentAttributeClassGt class HubitatCacGt(ComponentAttributeClassGt): diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index a43b3bd7..2fa12bfb 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -1,10 +1,7 @@ -import typing from typing import Literal, Optional import yarl -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.hubitat_component import HubitatComponent from gwproto.types.component_gt import ComponentGt from gwproto.types.hubitat_gt import HubitatGt from gwproto.types.rest_poller_gt import URLConfig @@ -13,10 +10,6 @@ class HubitatComponentGt(ComponentGt): Hubitat: HubitatGt TypeName: Literal["hubitat.component.gt"] = "hubitat.component.gt" - Version: Literal["000"] = "000" - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa def url_config(self) -> URLConfig: return self.Hubitat.url_config() @@ -33,28 +26,6 @@ def refresh_url_config(self, device_id: int) -> URLConfig: def refresh_url(self, device_id: int) -> yarl.URL: return self.Hubitat.refresh_url(device_id) - @classmethod - def from_data_class(cls, component: HubitatComponent) -> "HubitatComponentGt": - return HubitatComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - Hubitat=component.hubitat_gt, - DisplayName=component.display_name, - HwUid=component.hw_uid, - ) - - def to_data_class(self) -> HubitatComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(HubitatComponent, component) - return HubitatComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - hubitat_gt=self.Hubitat, - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) - @classmethod def make_stub(cls, component_id: str) -> "HubitatComponentGt": return HubitatComponentGt( @@ -68,25 +39,6 @@ def make_stub(cls, component_id: str) -> "HubitatComponentGt": ), ) - @classmethod - def from_component_id( - cls, component_id: str, components: dict[str, Component] - ) -> "HubitatComponent": - hubitat_component = components.get(component_id) - if hubitat_component is None: - raise ValueError( - "ERROR. No component found for " - f"HubitatTankComponent.hubitat.CompnentId {component_id}" - ) - if not isinstance(hubitat_component, HubitatComponent): - raise TypeError( - "ERROR. Referenced hubitat component has type " - f"{type(hubitat_component)}; " - "must be instance of HubitatComponent. " - f"Hubitat component id: {component_id}" - ) - return hubitat_component - class HubitatRESTResolutionSettings: component_gt: HubitatComponentGt diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py index 538067b9..0b58dde9 100644 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ b/src/gwproto/types/hubitat_poller_cac_gt.py @@ -1,6 +1,6 @@ from typing import Literal -from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types import ComponentAttributeClassGt class HubitatPollerCacGt(ComponentAttributeClassGt): diff --git a/src/gwproto/types/hubitat_poller_component_gt.py b/src/gwproto/types/hubitat_poller_component_gt.py index 3777c278..c8cbb391 100644 --- a/src/gwproto/types/hubitat_poller_component_gt.py +++ b/src/gwproto/types/hubitat_poller_component_gt.py @@ -1,11 +1,5 @@ -import copy -import typing from typing import Literal -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.hubitat_poller_component import ( - HubitatPollerComponent, -) from gwproto.types.component_gt import ComponentGt from gwproto.types.hubitat_poller_gt import HubitatPollerGt @@ -13,33 +7,3 @@ class HubitatPollerComponentGt(ComponentGt): Poller: HubitatPollerGt TypeName: Literal["hubitat.poller.component.gt"] = "hubitat.poller.component.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class( - cls, component: HubitatPollerComponent - ) -> "HubitatPollerComponentGt": - return HubitatPollerComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - Poller=copy.deepcopy(component.poller_gt), - DisplayName=component.display_name, - HwUid=component.hw_uid, - ) - - def to_data_class(self) -> HubitatPollerComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(HubitatPollerComponent, component) - return HubitatPollerComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - poller_gt=copy.deepcopy(self.Poller), - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) - - -class HubitatPollerComponentGt_Maker: - type_name = "hubitat.poller.component.gt" - version = "000" diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py index cc58132b..b22c67a1 100644 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ b/src/gwproto/types/hubitat_tank_cac_gt.py @@ -1,6 +1,6 @@ from typing import Literal -from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types import ComponentAttributeClassGt class HubitatTankCacGt(ComponentAttributeClassGt): diff --git a/src/gwproto/types/hubitat_tank_component_gt.py b/src/gwproto/types/hubitat_tank_component_gt.py index 771796dd..eae8b140 100644 --- a/src/gwproto/types/hubitat_tank_component_gt.py +++ b/src/gwproto/types/hubitat_tank_component_gt.py @@ -1,10 +1,5 @@ -import copy -import json -import typing -from typing import Any, Literal +from typing import Literal -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.hubitat_tank_component import HubitatTankComponent from gwproto.types.component_gt import ComponentGt from gwproto.types.hubitat_tank_gt import HubitatTankSettingsGt @@ -12,75 +7,3 @@ class HubitatTankComponentGt(ComponentGt): Tank: HubitatTankSettingsGt TypeName: Literal["hubitat.tank.component.gt"] = "hubitat.tank.component.gt" - Version: Literal["000"] = "000" - - def __hash__(self) -> int: - return hash((type(self), *tuple(self.__dict__.values()))) - - @classmethod - def from_data_class( - cls, component: HubitatTankComponent - ) -> "HubitatTankComponentGt": - return HubitatTankComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - Tank=HubitatTankSettingsGt( - hubitat_component_id=component.hubitat.ComponentId, - devices=copy.deepcopy(component.devices), - ), - DisplayName=component.display_name, - HwUid=component.hw_uid, - ) - - def to_data_class(self) -> HubitatTankComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(HubitatTankComponent, component) - return HubitatTankComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - tank_gt=self.Tank, - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) - - -class HubitatTankComponentGt_Maker: - type_name: str = HubitatTankComponentGt.model_fields["TypeName"].default - version = "000" - tuple: HubitatTankComponentGt - - def __init__(self, component: HubitatTankComponent) -> None: - self.tuple = HubitatTankComponentGt.from_data_class(component) - - @classmethod - def tuple_to_type(cls, tpl: HubitatTankComponentGt) -> str: - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: str) -> HubitatTankComponentGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> HubitatTankComponentGt: - return HubitatTankComponentGt(**d) - - @classmethod - def tuple_to_dc(cls, t: HubitatTankComponentGt) -> HubitatTankComponent: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: HubitatTankComponent) -> HubitatTankComponentGt: - return HubitatTankComponentGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> HubitatTankComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: HubitatTankComponent) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> HubitatTankComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py index 4f6c2a67..6368791f 100644 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ b/src/gwproto/types/multipurpose_sensor_component_gt.py @@ -1,354 +1,16 @@ """Type multipurpose.sensor.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, List, Literal, Optional +from typing import List, Literal -from pydantic import BaseModel, Field, field_validator - -from gwproto.data_classes.components.multipurpose_sensor_component import ( - MultipurposeSensorComponent, -) -from gwproto.errors import SchemaError +from gwproto.types import ComponentGt from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, - TelemetryReportingConfig_Maker, -) - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" ) -LOGGER = logging.getLogger(__name__) - - -class MultipurposeSensorComponentGt(BaseModel): - """ - Type for tracking Multupurpose Sensor Components. - - This type was first designed to work with a 12-channel analog temp sensor built into the - first generation GridWorks SCADA box (GSCADA 1). It extends the component.gt.000 type. Authority - for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, - HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' - structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable. The DisplayName is mutable, with its current value in time governed - by the WorldRegistry. - [More info](https://gridworks-protocol.readthedocs.io/en/latest/component.html) - """ - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of a MultipurposeSensor " - "(perhaps only the 12-channel analog temp sensor), and also as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - ChannelList: List[int] = Field( - title="ChannelList", - ) - ConfigList: List[TelemetryReportingConfig] = Field( - title="ConfigList", - ) - HwUid: Optional[str] = Field( - title="Hardware Unique Id", - default=None, - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description="Sample: Oak Multipurpose Temp Sensor Component <100>", - default=None, - ) +class MultipurposeSensorComponentGt(ComponentGt): + ChannelList: list[int] + ConfigList: List[TelemetryReportingConfig] TypeName: Literal["multipurpose.sensor.component.gt"] = ( "multipurpose.sensor.component.gt" ) - Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - multipurpose.sensor.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - multipurpose.sensor.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - # Recursively calling as_dict() - d["ConfigList"] = [elt.as_dict() for elt in self.ConfigList] - return d - - def as_type(self) -> bytes: - """ - Serialize to the multipurpose.sensor.component.gt.000 representation. - - Instances in the class are python-native representations of multipurpose.sensor.component.gt.000 - objects, while the actual multipurpose.sensor.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is MultipurposeSensorComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class MultipurposeSensorComponentGt_Maker: - type_name = "multipurpose.sensor.component.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - channel_list: List[int], - config_list: List[TelemetryReportingConfig], - hw_uid: Optional[str], - display_name: Optional[str], - ) -> None: - self.tuple = MultipurposeSensorComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - ChannelList=channel_list, - ConfigList=config_list, - HwUid=hw_uid, - DisplayName=display_name, - ) - - @classmethod - def tuple_to_type(cls, tpl: MultipurposeSensorComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> MultipurposeSensorComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> MultipurposeSensorComponentGt: # noqa: C901 - """ - Deserialize a dictionary representation of a multipurpose.sensor.component.gt.000 message object - into a MultipurposeSensorComponentGt python object for internal use. - - This is the near-inverse of the MultipurposeSensorComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a MultipurposeSensorComponentGt object. - - Returns: - MultipurposeSensorComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "ChannelList" not in d2: - raise SchemaError(f"dict missing ChannelList: <{d2}>") - if "ConfigList" not in d2: - raise SchemaError(f"dict missing ConfigList: <{d2}>") - if not isinstance(d2["ConfigList"], List): - raise SchemaError(f"ConfigList <{d2['ConfigList']}> must be a List!") - config_list = [] - for elt in d2["ConfigList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"ConfigList <{d2['ConfigList']}> must be a List of TelemetryReportingConfig types" - ) - t = TelemetryReportingConfig_Maker.dict_to_tuple(elt) - config_list.append(t) - d2["ConfigList"] = config_list - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret multipurpose.sensor.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return MultipurposeSensorComponentGt(**d2) - - @classmethod - def tuple_to_dc( - cls, t: MultipurposeSensorComponentGt - ) -> MultipurposeSensorComponent: - if t.ComponentId in MultipurposeSensorComponent.by_id: - dc = MultipurposeSensorComponent.by_id[t.ComponentId] - else: - dc = MultipurposeSensorComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - channel_list=t.ChannelList, - config_list=t.ConfigList, - hw_uid=t.HwUid, - display_name=t.DisplayName, - ) - return dc - - @classmethod - def dc_to_tuple( - cls, dc: MultipurposeSensorComponent - ) -> MultipurposeSensorComponentGt: - return MultipurposeSensorComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - channel_list=dc.channel_list, - config_list=dc.config_list, - hw_uid=dc.hw_uid, - display_name=dc.display_name, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> MultipurposeSensorComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode())) - - @classmethod - def dc_to_type(cls, dc: MultipurposeSensorComponent) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> MultipurposeSensorComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py index 83f61338..baa0851e 100644 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ b/src/gwproto/types/pipe_flow_sensor_component_gt.py @@ -1,309 +1,12 @@ """Type pipe.flow.sensor.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentGt -from gwproto.data_classes.components.pipe_flow_sensor_component import ( - PipeFlowSensorComponent, -) -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class PipeFlowSensorComponentGt(BaseModel): - """ - Type for tracking Pipe Flow Sensor Components. - - Designed for Pipe Flow Sensors. It extends the component.gt.000 type. Authority for the - attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, - HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' - structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable - it is meant to be an immutable identifier associated to a specific - physical device, ideally one that can be read remotely by the SCADA and also by the naked - eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry. - - [More info](https://gridworks-protocol.readthedocs.io/en/latest/component.html) - """ - - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of a PipeFlowSensor, " - "and also as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - I2cAddress: int = Field( - title="I2cAddress", - ) - ConversionFactor: float = Field( - title="ConversionFactor", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - description="Sample: Pipe Flow Meter Component ", - default=None, - ) - HwUid: Optional[str] = Field( - title="Hardware Unique Id", - default=None, - ) - ExpectedMaxGpmTimes100: Optional[int] = Field( - title="Expected Max Flow in Gallons per Minute, times 100", - default=None, - ) +class PipeFlowSensorComponentGt(ComponentGt): + I2cAddress: int + ConversionFactor: float + ExpectedMaxGpmTimes100: Optional[int] = None TypeName: Literal["pipe.flow.sensor.component.gt"] = "pipe.flow.sensor.component.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - pipe.flow.sensor.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - pipe.flow.sensor.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the pipe.flow.sensor.component.gt.000 representation. - - Instances in the class are python-native representations of pipe.flow.sensor.component.gt.000 - objects, while the actual pipe.flow.sensor.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is PipeFlowSensorComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class PipeFlowSensorComponentGt_Maker: - type_name = "pipe.flow.sensor.component.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - i2c_address: int, - conversion_factor: float, - display_name: Optional[str], - hw_uid: Optional[str], - expected_max_gpm_times100: Optional[int], - ) -> None: - self.tuple = PipeFlowSensorComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - I2cAddress=i2c_address, - ConversionFactor=conversion_factor, - DisplayName=display_name, - HwUid=hw_uid, - ExpectedMaxGpmTimes100=expected_max_gpm_times100, - ) - - @classmethod - def tuple_to_type(cls, tpl: PipeFlowSensorComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> PipeFlowSensorComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> PipeFlowSensorComponentGt: - """ - Deserialize a dictionary representation of a pipe.flow.sensor.component.gt.000 message object - into a PipeFlowSensorComponentGt python object for internal use. - - This is the near-inverse of the PipeFlowSensorComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a PipeFlowSensorComponentGt object. - - Returns: - PipeFlowSensorComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "I2cAddress" not in d2: - raise SchemaError(f"dict missing I2cAddress: <{d2}>") - if "ConversionFactor" not in d2: - raise SchemaError(f"dict missing ConversionFactor: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret pipe.flow.sensor.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return PipeFlowSensorComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: PipeFlowSensorComponentGt) -> PipeFlowSensorComponent: - if t.ComponentId in PipeFlowSensorComponent.by_id: - dc = PipeFlowSensorComponent.by_id[t.ComponentId] - else: - dc = PipeFlowSensorComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - i2c_address=t.I2cAddress, - conversion_factor=t.ConversionFactor, - display_name=t.DisplayName, - hw_uid=t.HwUid, - expected_max_gpm_times100=t.ExpectedMaxGpmTimes100, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: PipeFlowSensorComponent) -> PipeFlowSensorComponentGt: - return PipeFlowSensorComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - i2c_address=dc.i2c_address, - conversion_factor=dc.conversion_factor, - display_name=dc.display_name, - hw_uid=dc.hw_uid, - expected_max_gpm_times100=dc.expected_max_gpm_times100, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> PipeFlowSensorComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: PipeFlowSensorComponent) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> PipeFlowSensorComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py index bbbbdb74..af20154f 100644 --- a/src/gwproto/types/relay_component_gt.py +++ b/src/gwproto/types/relay_component_gt.py @@ -1,303 +1,11 @@ """Type relay.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentGt -from gwproto.data_classes.components.relay_component import RelayComponent -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class RelayComponentGt(BaseModel): - """ - Type for tracking Relay Components. - - Designed for Relays. It extends the component.gt.000 type. Authority for the attributes - of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs - to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure - for managing relational device data . Notably, ComponentId and ComponentAttributeClass are - both required and immutable. HwUid is optional but once it is set to a non-null value that - is also immutable - it is meant to be an immutable identifier associated to a specific physical - device, ideally one that can be read remotely by the SCADA and also by the naked eye. The - DisplayName is mutable, with its current value in time governed by the WorldRegistry. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component.html) - """ - - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of a Relay, and also " - "as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - Gpio: Optional[int] = Field( - title="Gpio", - default=None, - ) - HwUid: Optional[str] = Field( - title="Hardware Unique Id", - default=None, - ) - NormallyOpen: bool = Field( - title="Normally Open", - description=( - "Normally open relays default in the open position, meaning that when they're not " - "in use, there is no contact between the circuits. When power is introduced, an electromagnet " - "pulls the first circuit into contact with the second, thereby closing the circuit " - "and allowing power to flow through" - ), - ) +class RelayComponentGt(ComponentGt): + Gpio: Optional[int] = None + NormallyOpen: bool TypeName: Literal["relay.component.gt"] = "relay.component.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - relay.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - relay.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the relay.component.gt.000 representation. - - Instances in the class are python-native representations of relay.component.gt.000 - objects, while the actual relay.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is RelayComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class RelayComponentGt_Maker: - type_name = "relay.component.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str], - gpio: Optional[int], - hw_uid: Optional[str], - normally_open: bool, # noqa: FBT001 - ) -> None: - self.tuple = RelayComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - Gpio=gpio, - HwUid=hw_uid, - NormallyOpen=normally_open, - ) - - @classmethod - def tuple_to_type(cls, tpl: RelayComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> RelayComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> RelayComponentGt: - """ - Deserialize a dictionary representation of a relay.component.gt.000 message object - into a RelayComponentGt python object for internal use. - - This is the near-inverse of the RelayComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a RelayComponentGt object. - - Returns: - RelayComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "NormallyOpen" not in d2: - raise SchemaError(f"dict missing NormallyOpen: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret relay.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return RelayComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: RelayComponentGt) -> RelayComponent: - if t.ComponentId in RelayComponent.by_id: - dc = RelayComponent.by_id[t.ComponentId] - else: - dc = RelayComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - gpio=t.Gpio, - hw_uid=t.HwUid, - normally_open=t.NormallyOpen, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: RelayComponent) -> RelayComponentGt: - return RelayComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - gpio=dc.gpio, - hw_uid=dc.hw_uid, - normally_open=dc.normally_open, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> RelayComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) or t - - @classmethod - def dc_to_type(cls, dc: RelayComponent) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> RelayComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/resistive_heater_component_gt.py b/src/gwproto/types/resistive_heater_component_gt.py index 54768b37..12e0cf74 100644 --- a/src/gwproto/types/resistive_heater_component_gt.py +++ b/src/gwproto/types/resistive_heater_component_gt.py @@ -1,298 +1,11 @@ """Type resistive.heater.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentGt -from gwproto.data_classes.components.resistive_heater_component import ( - ResistiveHeaterComponent, -) -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class ResistiveHeaterComponentGt(BaseModel): - """ - Type for tracking Resistive Heater Components. - - Designed for Resistive Heaters. It extends the component.gt.000 type. Authority for the - attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, - HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' - structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable - it is meant to be an immutable identifier associated to a specific - physical device, ideally one that can be read remotely by the SCADA and also by the naked - eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component.html) - """ - - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of a ResistiveHeater, " - "and also as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - HwUid: Optional[str] = Field( - title="Hardware Unique Id", - default=None, - ) - TestedMaxHotMilliOhms: Optional[int] = Field( - title="TestedMaxHotMilliOhms", - default=None, - ) - TestedMaxColdMilliOhms: Optional[int] = Field( - title="TestedMaxColdMilliOhms", - default=None, - ) +class ResistiveHeaterComponentGt(ComponentGt): + TestedMaxHotMilliOhms: Optional[int] = None + TestedMaxColdMilliOhms: Optional[int] = None TypeName: Literal["resistive.heater.component.gt"] = "resistive.heater.component.gt" - Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - resistive.heater.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - resistive.heater.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the resistive.heater.component.gt.000 representation. - - Instances in the class are python-native representations of resistive.heater.component.gt.000 - objects, while the actual resistive.heater.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is ResistiveHeaterComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class ResistiveHeaterComponentGt_Maker: - type_name = "resistive.heater.component.gt" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str], - hw_uid: Optional[str], - tested_max_hot_milli_ohms: Optional[int], - tested_max_cold_milli_ohms: Optional[int], - ) -> None: - self.tuple = ResistiveHeaterComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - HwUid=hw_uid, - TestedMaxHotMilliOhms=tested_max_hot_milli_ohms, - TestedMaxColdMilliOhms=tested_max_cold_milli_ohms, - ) - - @classmethod - def tuple_to_type(cls, tpl: ResistiveHeaterComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> ResistiveHeaterComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> ResistiveHeaterComponentGt: - """ - Deserialize a dictionary representation of a resistive.heater.component.gt.000 message object - into a ResistiveHeaterComponentGt python object for internal use. - - This is the near-inverse of the ResistiveHeaterComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a ResistiveHeaterComponentGt object. - - Returns: - ResistiveHeaterComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret resistive.heater.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return ResistiveHeaterComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: ResistiveHeaterComponentGt) -> ResistiveHeaterComponent: - if t.ComponentId in ResistiveHeaterComponent.by_id: - dc = ResistiveHeaterComponent.by_id[t.ComponentId] - else: - dc = ResistiveHeaterComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - hw_uid=t.HwUid, - tested_max_hot_milli_ohms=t.TestedMaxHotMilliOhms, - tested_max_cold_milli_ohms=t.TestedMaxColdMilliOhms, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ResistiveHeaterComponent) -> ResistiveHeaterComponentGt: - return ResistiveHeaterComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - hw_uid=dc.hw_uid, - tested_max_hot_milli_ohms=dc.tested_max_hot_milli_ohms, - tested_max_cold_milli_ohms=dc.tested_max_cold_milli_ohms, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ResistiveHeaterComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: ResistiveHeaterComponent) -> str: - return cls.dc_to_tuple(dc).as_type() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ResistiveHeaterComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/rest_poller_component_gt.py b/src/gwproto/types/rest_poller_component_gt.py index 67010d7a..b25c7649 100644 --- a/src/gwproto/types/rest_poller_component_gt.py +++ b/src/gwproto/types/rest_poller_component_gt.py @@ -3,84 +3,12 @@ """ -import json -import typing -from typing import Any, Literal, Optional +from typing import Literal -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.rest_poller_component import RESTPollerComponent from gwproto.types import ComponentGt from gwproto.types.rest_poller_gt import RESTPollerSettings class RESTPollerComponentGt(ComponentGt): - ComponentId: str - ComponentAttributeClassId: str - DisplayName: Optional[str] = None - HwUid: Optional[str] = None Rest: RESTPollerSettings TypeName: Literal["rest.poller.component.gt"] = "rest.poller.component.gt" - Version: Literal["000"] = "000" - - @classmethod - def from_data_class(cls, component: RESTPollerComponent) -> "RESTPollerComponentGt": - return RESTPollerComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - DisplayName=component.display_name, - HwUid=component.hw_uid, - Rest=component.rest, - ) - - def to_data_class(self) -> RESTPollerComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(RESTPollerComponent, component) - return RESTPollerComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - rest=self.Rest, - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) - - -class RESTPollerComponentGt_Maker: - type_name: str = RESTPollerComponentGt.model_fields["TypeName"].default - version = "000" - tuple: RESTPollerComponentGt - - def __init__(self, component: RESTPollerComponent) -> None: - self.tuple = RESTPollerComponentGt.from_data_class(component) - - @classmethod - def tuple_to_type(cls, tpl: RESTPollerComponentGt) -> str: - return tpl.as_type().decode() - - @classmethod - def type_to_tuple(cls, t: str) -> RESTPollerComponentGt: - return cls.dict_to_tuple(json.loads(t)) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> RESTPollerComponentGt: - return RESTPollerComponentGt(**d) - - @classmethod - def tuple_to_dc(cls, t: RESTPollerComponentGt) -> RESTPollerComponent: - return t.to_data_class() - - @classmethod - def dc_to_tuple(cls, dc: RESTPollerComponent) -> RESTPollerComponentGt: - return RESTPollerComponentGt.from_data_class(dc) - - @classmethod - def type_to_dc(cls, t: str) -> RESTPollerComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: RESTPollerComponent) -> str: - return cls.dc_to_tuple(dc).as_type().decode() - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> RESTPollerComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py index f99372ec..3eed5cf2 100644 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ b/src/gwproto/types/simple_temp_sensor_component_gt.py @@ -1,292 +1,12 @@ """Type simple.temp.sensor.component.gt, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from gwproto.types import ComponentGt -from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorComponent, -) -from gwproto.errors import SchemaError -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - - -class SimpleTempSensorComponentGt(BaseModel): - """ - Type for tracking Simple Temp Sensor Components. - - Designed for simple temp sensors that read only one temp. It extends the component.gt.000 - type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, - DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks - 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass - are both required and immutable. HwUid is optional but once it is set to a non-null value - that is also immutable - it is meant to be an immutable identifier associated to a specific - physical device, ideally one that can be read remotely by the SCADA and also by the naked - eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry. - - [More info](https://g-node-registry.readthedocs.io/en/latest/component.html) - """ - - ComponentId: str = Field( - title="Component Id", - description=( - "Primary GridWorks identifier for a specific physical instance of a SimpleTempSensor, " - "and also as a more generic Component." - ), - ) - ComponentAttributeClassId: str = Field( - title="ComponentAttributeClassId", - description=( - "Unique identifier for the device class. Authority for these, as well as the relationship " - "between Components and ComponentAttributeClasses (Cacs) is maintained by the World " - "Registry." - ), - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - HwUid: Optional[str] = Field( - title="Hardware Unique Id", - default=None, - ) - Channel: Optional[int] = Field( - title="Channel", - default=None, - ) +class SimpleTempSensorComponentGt(ComponentGt): + Channel: Optional[int] = None TypeName: Literal["simple.temp.sensor.component.gt"] = ( "simple.temp.sensor.component.gt" ) - Version: Literal["000"] = "000" - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("ComponentAttributeClassId") - @classmethod - def _check_component_attribute_class_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentAttributeClassId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - simple.temp.sensor.component.gt.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - simple.temp.sensor.component.gt.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the simple.temp.sensor.component.gt.000 representation. - - Instances in the class are python-native representations of simple.temp.sensor.component.gt.000 - objects, while the actual simple.temp.sensor.component.gt.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is SimpleTempSensorComponentGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class SimpleTempSensorComponentGt_Maker: - type_name = "simple.temp.sensor.component.gt" - version = "000" - - def __init__( - self, - component_id: str, - component_attribute_class_id: str, - display_name: Optional[str], - hw_uid: Optional[str], - channel: Optional[int], - ) -> None: - self.tuple = SimpleTempSensorComponentGt( - ComponentId=component_id, - ComponentAttributeClassId=component_attribute_class_id, - DisplayName=display_name, - HwUid=hw_uid, - Channel=channel, - ) - - @classmethod - def tuple_to_type(cls, tpl: SimpleTempSensorComponentGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> SimpleTempSensorComponentGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> SimpleTempSensorComponentGt: - """ - Deserialize a dictionary representation of a simple.temp.sensor.component.gt.000 message object - into a SimpleTempSensorComponentGt python object for internal use. - - This is the near-inverse of the SimpleTempSensorComponentGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a SimpleTempSensorComponentGt object. - - Returns: - SimpleTempSensorComponentGt - """ - d2 = dict(d) - if "ComponentId" not in d2: - raise SchemaError(f"dict missing ComponentId: <{d2}>") - if "ComponentAttributeClassId" not in d2: - raise SchemaError(f"dict missing ComponentAttributeClass: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret simple.temp.sensor.component.gt version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return SimpleTempSensorComponentGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: SimpleTempSensorComponentGt) -> SimpleTempSensorComponent: - if t.ComponentId in SimpleTempSensorComponent.by_id: - dc = SimpleTempSensorComponent.by_id[t.ComponentId] - else: - dc = SimpleTempSensorComponent( - component_id=t.ComponentId, - component_attribute_class_id=t.ComponentAttributeClassId, - display_name=t.DisplayName, - hw_uid=t.HwUid, - channel=t.Channel, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: SimpleTempSensorComponent) -> SimpleTempSensorComponentGt: - return SimpleTempSensorComponentGt_Maker( - component_id=dc.component_id, - component_attribute_class_id=dc.component_attribute_class_id, - display_name=dc.display_name, - hw_uid=dc.hw_uid, - channel=dc.channel, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> SimpleTempSensorComponent: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) - - @classmethod - def dc_to_type(cls, dc: SimpleTempSensorComponent) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> SimpleTempSensorComponent: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 8881ad14..8e4fab8d 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -1,414 +1,24 @@ """Type spaceheat.node.gt, version 100""" -import json -import logging -from typing import Any, Dict, Literal, Optional +from typing import Literal, Optional -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.data_classes.sh_node import ShNode -from gwproto.enums import ActorClass as EnumActorClass -from gwproto.enums import Role as EnumRole -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.enums import ActorClass, Role +from gwproto.property_format import LeftRightDotStr, UUID4Str class SpaceheatNodeGt(BaseModel): - """ - Spaceheat Node. - - A SpaceheatNode, or ShNode, is an organizing principal for the SCADA software. ShNodes can - represent both underlying physical objects (water tank), measurements of these objects (temperature - sensing at the top of a water tank), and actors within the code (an actor measuring multiple - temperatures, or an actor responsible for filtering/smoothing temperature data for the purposes - of thermostatic control). - - [More info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html) - """ - - ShNodeId: str = Field( - title="ShNodeId", - ) - Alias: str = Field( - title="Alias", - ) - ActorClass: EnumActorClass = Field( - title="ActorClass", - ) - Role: EnumRole = Field( - title="Role", - ) - DisplayName: Optional[str] = Field( - title="DisplayName", - default=None, - ) - ComponentId: Optional[str] = Field( - title="Unique identifier for Spaceheat Node's Component", - description="Used if a Spaceheat Node is associated with a physical device.", - default=None, - ) - ReportingSamplePeriodS: Optional[int] = Field( - title="ReportingSamplePeriodS", - default=None, - ) - RatedVoltageV: Optional[int] = Field( - title="RatedVoltageV", - default=None, - ) - TypicalVoltageV: Optional[int] = Field( - title="TypicalVoltageV", - default=None, - ) + ShNodeId: UUID4Str + Alias: LeftRightDotStr + ActorClass: ActorClass + Role: Role + DisplayName: Optional[str] = None + ComponentId: Optional[UUID4Str] = None + ReportingSamplePeriodS: Optional[int] = None InPowerMetering: Optional[bool] = Field( title="InPowerMetering", default=None, ) TypeName: Literal["spaceheat.node.gt"] = "spaceheat.node.gt" Version: Literal["100"] = "100" - - @field_validator("ShNodeId") - @classmethod - def _check_sh_node_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ShNodeId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("Alias") - @classmethod - def _check_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"Alias failed LeftRightDot format validation: {e}") - return v - - @field_validator("ComponentId") - @classmethod - def _check_component_id(cls, v: Optional[str]) -> Optional[str]: - if v is None: - return v - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"ComponentId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("RatedVoltageV") - @classmethod - def _check_rated_voltage_v(cls, v: Optional[int]) -> Optional[int]: - if v is None: - return v - try: - check_is_positive_integer(v) - except ValueError as e: - raise ValueError( - f"RatedVoltageV failed PositiveInteger format validation: {e}" - ) - return v - - @field_validator("TypicalVoltageV") - @classmethod - def _check_typical_voltage_v(cls, v: Optional[int]) -> Optional[int]: - if v is None: - return v - try: - check_is_positive_integer(v) - except ValueError as e: - raise ValueError( - f"TypicalVoltageV failed PositiveInteger format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - spaceheat.node.gt.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - spaceheat.node.gt.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["ActorClass"] - d["ActorClassGtEnumSymbol"] = EnumActorClass.value_to_symbol(self.ActorClass) - del d["Role"] - d["RoleGtEnumSymbol"] = EnumRole.value_to_symbol(self.Role) - return d - - def as_type(self) -> bytes: - """ - Serialize to the spaceheat.node.gt.100 representation. - - Instances in the class are python-native representations of spaceheat.node.gt.100 - objects, while the actual spaceheat.node.gt.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is SpaceheatNodeGt.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class SpaceheatNodeGt_Maker: - type_name = "spaceheat.node.gt" - version = "100" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - sh_node_id: str, - alias: str, - actor_class: EnumActorClass, - role: EnumRole, - display_name: Optional[str], - component_id: Optional[str], - reporting_sample_period_s: Optional[int], - rated_voltage_v: Optional[int], - typical_voltage_v: Optional[int], - in_power_metering: Optional[bool], - ) -> None: - self.tuple = SpaceheatNodeGt( - ShNodeId=sh_node_id, - Alias=alias, - ActorClass=actor_class, - Role=role, - DisplayName=display_name, - ComponentId=component_id, - ReportingSamplePeriodS=reporting_sample_period_s, - RatedVoltageV=rated_voltage_v, - TypicalVoltageV=typical_voltage_v, - InPowerMetering=in_power_metering, - ) - - @classmethod - def tuple_to_type(cls, tpl: SpaceheatNodeGt) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> SpaceheatNodeGt: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> SpaceheatNodeGt: - """ - Deserialize a dictionary representation of a spaceheat.node.gt.100 message object - into a SpaceheatNodeGt python object for internal use. - - This is the near-inverse of the SpaceheatNodeGt.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a SpaceheatNodeGt object. - - Returns: - SpaceheatNodeGt - """ - d2 = dict(d) - if "ShNodeId" not in d2: - raise SchemaError(f"dict missing ShNodeId: <{d2}>") - if "Alias" not in d2: - raise SchemaError(f"dict missing Alias: <{d2}>") - if "ActorClassGtEnumSymbol" not in d2: - raise SchemaError(f"ActorClassGtEnumSymbol missing from dict <{d2}>") - value = EnumActorClass.symbol_to_value(d2["ActorClassGtEnumSymbol"]) - d2["ActorClass"] = EnumActorClass(value) - if "RoleGtEnumSymbol" not in d2: - raise SchemaError(f"RoleGtEnumSymbol missing from dict <{d2}>") - value = EnumRole.symbol_to_value(d2["RoleGtEnumSymbol"]) - d2["Role"] = EnumRole(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret spaceheat.node.gt version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return SpaceheatNodeGt(**d2) - - @classmethod - def tuple_to_dc(cls, t: SpaceheatNodeGt) -> ShNode: - if t.ShNodeId in ShNode.by_id: - dc = ShNode.by_id[t.ShNodeId] - else: - dc = ShNode( - sh_node_id=t.ShNodeId, - alias=t.Alias, - actor_class=t.ActorClass, - role=t.Role, - display_name=t.DisplayName, - component_id=t.ComponentId, - reporting_sample_period_s=t.ReportingSamplePeriodS, - rated_voltage_v=t.RatedVoltageV, - typical_voltage_v=t.TypicalVoltageV, - in_power_metering=t.InPowerMetering, - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ShNode) -> SpaceheatNodeGt: - return SpaceheatNodeGt_Maker( - sh_node_id=dc.sh_node_id, - alias=dc.alias, - actor_class=dc.actor_class, - role=dc.role, - display_name=dc.display_name, - component_id=dc.component_id, - reporting_sample_period_s=dc.reporting_sample_period_s, - rated_voltage_v=dc.rated_voltage_v, - typical_voltage_v=dc.typical_voltage_v, - in_power_metering=dc.in_power_metering, - ).tuple - - @classmethod - def type_to_dc(cls, t: str) -> ShNode: - return cls.tuple_to_dc(cls.type_to_tuple(t.encode("utf-8"))) - - @classmethod - def dc_to_type(cls, dc: ShNode) -> str: - return cls.dc_to_tuple(dc).as_type().decode("utf-8") - - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> ShNode: - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index 8b390f69..a5785deb 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -1,83 +1,27 @@ """Type telemetry.reporting.config, version 000""" -import json -import logging -from typing import Any, Dict, Literal, Optional, Self +from typing import Literal, Optional, Self -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel, PositiveInt, model_validator -from gwproto.enums import TelemetryName as EnumTelemetryName -from gwproto.enums import Unit as EnumUnit -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.enums import TelemetryName, Unit +from gwproto.property_format import LeftRightDotStr class TelemetryReportingConfig(BaseModel): - """ """ - - TelemetryName: EnumTelemetryName = Field( - title="TelemetryName", - ) - AboutNodeName: str = Field( - title="AboutNodeName", - description="The name of the SpaceheatNode whose physical quantity is getting captured.", - ) - ReportOnChange: bool = Field( - title="ReportOnChange", - ) - SamplePeriodS: int = Field( - title="SamplePeriodS", - ) - Exponent: int = Field( - title="Exponent", - description=( - "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " - "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " - "would indicate 65.3 deg C" - ), - ) - Unit: EnumUnit = Field( - title="Unit", - ) - AsyncReportThreshold: Optional[float] = Field( - title="AsyncReportThreshold", - default=None, - ) - NameplateMaxValue: Optional[int] = Field( - title="NameplateMaxValue", - default=None, - ) + TelemetryName: TelemetryName + AboutNodeName: LeftRightDotStr + ReportOnChange: bool + SamplePeriodS: int + Exponent: int + Unit: Unit + AsyncReportThreshold: Optional[float] = None + NameplateMaxValue: Optional[PositiveInt] = None TypeName: Literal["telemetry.reporting.config"] = "telemetry.reporting.config" Version: Literal["000"] = "000" - @field_validator("AboutNodeName") - @classmethod - def _check_about_node_name(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"AboutNodeName failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("NameplateMaxValue") - @classmethod - def _check_nameplate_max_value(cls, v: Optional[int]) -> Optional[int]: - if v is None: - return v - try: - check_is_positive_integer(v) - except ValueError as e: - raise ValueError( - f"NameplateMaxValue failed PositiveInteger format validation: {e}" - ) - return v + def __hash__(self) -> int: + return hash((type(self), *self.__dict__.values())) @model_validator(mode="after") def check_axiom_1(self) -> Self: @@ -90,207 +34,3 @@ def check_axiom_1(self) -> Self: "Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" ) return self - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - telemetry.reporting.config.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - telemetry.reporting.config.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["TelemetryName"] - d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( - self.TelemetryName - ) - del d["Unit"] - d["UnitGtEnumSymbol"] = EnumUnit.value_to_symbol(self.Unit) - return d - - def as_type(self) -> bytes: - """ - Serialize to the telemetry.reporting.config.000 representation. - - Instances in the class are python-native representations of telemetry.reporting.config.000 - objects, while the actual telemetry.reporting.config.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is TelemetryReportingConfig.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class TelemetryReportingConfig_Maker: - type_name = "telemetry.reporting.config" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - telemetry_name: EnumTelemetryName, - about_node_name: str, - report_on_change: bool, # noqa: FBT001 - sample_period_s: int, - exponent: int, - unit: EnumUnit, - async_report_threshold: Optional[float], - nameplate_max_value: Optional[int], - ) -> None: - self.tuple = TelemetryReportingConfig( - TelemetryName=telemetry_name, - AboutNodeName=about_node_name, - ReportOnChange=report_on_change, - SamplePeriodS=sample_period_s, - Exponent=exponent, - Unit=unit, - AsyncReportThreshold=async_report_threshold, - NameplateMaxValue=nameplate_max_value, - ) - - @classmethod - def tuple_to_type(cls, tpl: TelemetryReportingConfig) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> TelemetryReportingConfig: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> TelemetryReportingConfig: - """ - Deserialize a dictionary representation of a telemetry.reporting.config.000 message object - into a TelemetryReportingConfig python object for internal use. - - This is the near-inverse of the TelemetryReportingConfig.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a TelemetryReportingConfig object. - - Returns: - TelemetryReportingConfig - """ - d2 = dict(d) - if "TelemetryNameGtEnumSymbol" not in d2: - raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") - value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) - d2["TelemetryName"] = EnumTelemetryName(value) - if "AboutNodeName" not in d2: - raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "ReportOnChange" not in d2: - raise SchemaError(f"dict missing ReportOnChange: <{d2}>") - if "SamplePeriodS" not in d2: - raise SchemaError(f"dict missing SamplePeriodS: <{d2}>") - if "Exponent" not in d2: - raise SchemaError(f"dict missing Exponent: <{d2}>") - if "UnitGtEnumSymbol" not in d2: - raise SchemaError(f"UnitGtEnumSymbol missing from dict <{d2}>") - value = EnumUnit.symbol_to_value(d2["UnitGtEnumSymbol"]) - d2["Unit"] = EnumUnit(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret telemetry.reporting.config version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return TelemetryReportingConfig(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py index be1674d1..caca9c2e 100644 --- a/src/gwproto/types/web_server_cac_gt.py +++ b/src/gwproto/types/web_server_cac_gt.py @@ -1,6 +1,6 @@ from typing import Literal -from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types import ComponentAttributeClassGt class WebServerCacGt(ComponentAttributeClassGt): diff --git a/src/gwproto/types/web_server_component_gt.py b/src/gwproto/types/web_server_component_gt.py index 45772558..117d4777 100644 --- a/src/gwproto/types/web_server_component_gt.py +++ b/src/gwproto/types/web_server_component_gt.py @@ -1,8 +1,5 @@ -import typing from typing import Literal -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.web_server_component import WebServerComponent from gwproto.types.component_gt import ComponentGt from gwproto.types.web_server_gt import WebServerGt @@ -10,29 +7,3 @@ class WebServerComponentGt(ComponentGt): WebServer: WebServerGt TypeName: Literal["web.server.component.gt"] = "web.server.component.gt" - Version: Literal["000"] = "000" - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - @classmethod - def from_data_class(cls, component: WebServerComponent) -> "WebServerComponentGt": - return WebServerComponentGt( - ComponentId=component.component_id, - ComponentAttributeClassId=component.component_attribute_class_id, - WebServer=component.web_server_gt, - DisplayName=component.display_name, - HwUid=component.hw_uid, - ) - - def to_data_class(self) -> WebServerComponent: - component = Component.by_id.get(self.ComponentId, None) - if component is not None: - return typing.cast(WebServerComponent, component) - return WebServerComponent( - component_id=self.ComponentId, - component_attribute_class_id=self.ComponentAttributeClassId, - web_server_gt=self.WebServer, - display_name=self.DisplayName, - hw_uid=self.HwUid, - ) diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index 4306e4de..eb2c18c2 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -1,8 +1,4 @@ import re -import uuid -from typing import Annotated - -from pydantic import BeforeValidator snake_add_underscore_to_camel_pattern = re.compile(r"(? str: def has_mac_address_format(mac_str: str) -> bool: return bool(MAC_REGEX.match(mac_str.lower())) - - -def str_is_valid_uuid4(v: str) -> str: - v = str(v) - try: - u = uuid.UUID(v) - except Exception as e: - raise ValueError(f"Invalid UUID4: {v} <{e}>") from e - if u.version != 4: - raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") - return str(u) - - -UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] diff --git a/tests/cac_load_utils.py b/tests/cac_load_utils.py index 57ddc227..112cafa2 100644 --- a/tests/cac_load_utils.py +++ b/tests/cac_load_utils.py @@ -1,10 +1,7 @@ from dataclasses import dataclass, field from typing import Optional, Type -from gwproto import CacDecoder, default_cac_decoder -from gwproto.data_classes.hardware_layout import ( - load_cacs, -) +from gwproto import CacDecoder, HardwareLayout, default_cac_decoder from gwproto.types import ComponentAttributeClassGt @@ -68,7 +65,7 @@ def _decode_cac(case: CacCase, decoder: Optional[CacDecoder]) -> CacLoadResult: ) cac_id = cac_dict["ComponentAttributeClassId"] try: - loaded_cac = load_cacs( + loaded_cac = HardwareLayout.load_cacs( layout={"OtherCacs": [cac_dict]}, raise_errors=True, cac_decoder=decoder, diff --git a/tests/component_load_utils.py b/tests/component_load_utils.py new file mode 100644 index 00000000..e27a6d35 --- /dev/null +++ b/tests/component_load_utils.py @@ -0,0 +1,138 @@ +from dataclasses import dataclass, field +from typing import Optional, Type + +from gwproto import ( + ComponentDecoder, + HardwareLayout, + default_component_decoder, +) +from gwproto.data_classes.components import Component +from gwproto.types import ComponentAttributeClassGt, ComponentGt + + +@dataclass +class ComponentCase: + tag: str + src_component_gt: ComponentGt | dict + exp_component_gt_type: Type = ComponentGt + exp_component_type: Type = Component + exp_component: Optional[Component] = None + exp_exceptions: list[Type[Exception]] = field(default_factory=list) + + +@dataclass +class ComponentCaseError: + case_idx: int + case: ComponentCase + + def __str__(self) -> str: + return f"{self.case.tag:30s} {self.case_idx:2d} {type(self)}" + + +@dataclass +class ComponentLoadError(ComponentCaseError): + exception: Exception + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\t{type(self.exception)}" + f"\n\t\t{self.exception}" + ) + + +@dataclass +class ComponentMatchError(ComponentCaseError): + exp_component: Component | dict + loaded_component: Component + + def __str__(self) -> str: + return ( + f"{super().__str__()}" + f"\n\t\texp: {type(self.exp_component)}" + f"\n\t\tgot: {type(self.loaded_component)}" + ) + + +@dataclass +class ComponentLoadResult: + ok: bool + loaded: Component | None + exception: Exception | None + + +def _decode_component( + case: ComponentCase, + decoder: Optional[ComponentDecoder], + cacs: dict[str, ComponentAttributeClassGt], +) -> ComponentLoadResult: + if decoder is None: + decoder = default_component_decoder + component_dict = ( + case.src_component_gt.model_dump() + if isinstance(case.src_component_gt, ComponentGt) + else case.src_component_gt + ) + component_id = component_dict["ComponentId"] + try: + loaded_component = HardwareLayout.load_components( + layout={"OtherComponents": [component_dict]}, + cacs=cacs, + raise_errors=True, + component_decoder=decoder, + )[component_id] + exception = None + except Exception as e: # noqa: BLE001 + loaded_component = None + exception = e + if loaded_component is None: + ok = type(exception) in case.exp_exceptions + else: + ok = not case.exp_exceptions + return ComponentLoadResult(ok, loaded_component, exception) + + +def assert_component_load( + cases: list[ComponentCase], + decoder: Optional[ComponentDecoder] = None, + cacs: Optional[dict[str, ComponentAttributeClassGt]] = None, +) -> None: + errors: list[ComponentCaseError] = [] + if cacs is None: + cacs = HardwareLayout.load("tests/config/hardware-layout.json").cacs + for case_idx, case in enumerate(cases): + load_result = _decode_component(case, decoder, cacs) + if not load_result.ok: + errors.append(ComponentLoadError(case_idx, case, load_result.exception)) + elif not case.exp_exceptions: + exp_component_gt = ( + case.src_component_gt + if case.exp_component is None + else case.exp_component + ) + if isinstance(exp_component_gt, dict): + exp_component_gt = case.exp_component_gt_type(**exp_component_gt) + if case.exp_component is None: + cac = cacs[exp_component_gt.ComponentAttributeClassId] + exp_component = case.exp_component_type(gt=exp_component_gt, cac=cac) + else: + exp_component = case.exp_component + if load_result.loaded != exp_component: + errors.append( + ComponentMatchError( + case_idx=case_idx, + case=case, + exp_component=exp_component, + loaded_component=load_result.loaded, + ) + ) + if errors: + err_str = "ERROR. Got component load/matching errors:" + first_exception = None + for error in errors: + err_str += f"\n\t{error}" + if first_exception is None and hasattr(error, "exception"): + first_exception = error.exception + if first_exception is not None: + raise ValueError(err_str) from first_exception + raise ValueError(err_str) diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index df7154e6..61951421 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -23,8 +23,8 @@ "ShNodes": [ { "Alias": "a", - "RoleGtEnumSymbol": "6ddff83b", - "ActorClassGtEnumSymbol": "b103058f", + "Role": "Atn", + "ActorClass": "Atn", "DisplayName": "AtomicTNode", "ShNodeId": "b354edeb-0c82-4e55-80cb-7ab669ac2ad9", "TypeName": "spaceheat.node.gt", @@ -32,8 +32,8 @@ }, { "Alias": "a.home", - "RoleGtEnumSymbol": "863e50d1", - "ActorClassGtEnumSymbol": "32d3d19f", + "Role": "HomeAlone", + "ActorClass": "HomeAlone", "DisplayName": "Little Orange House HomeAlone", "ShNodeId": "34470c9d-fa25-4077-909b-2f981a691d7e", "TypeName": "spaceheat.node.gt", @@ -41,8 +41,8 @@ }, { "Alias": "a.s", - "RoleGtEnumSymbol": "d0afb424", - "ActorClassGtEnumSymbol": "6d37aa41", + "Role": "Scada", + "ActorClass": "Scada", "DisplayName": "Little Orange House Main Scada", "ShNodeId": "259f9431-c6a1-4170-8766-04cbf65cff4a", "TypeName": "spaceheat.node.gt", @@ -50,8 +50,8 @@ }, { "Alias": "a.elt1", - "RoleGtEnumSymbol": "99c5f326", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BoostElement", + "ActorClass": "NoActor", "DisplayName": "First boost element", "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", @@ -63,8 +63,8 @@ }, { "Alias": "a.elt1.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BooleanActuator", + "ActorClass": "NoActor", "DisplayName": "30A Relay for first boost element", "ShNodeId": "e077df5d-7aea-4b9c-bfd7-773c1e65bd65", "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", @@ -73,8 +73,8 @@ }, { "Alias": "a.elt2", - "RoleGtEnumSymbol": "99c5f326", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BoostElement", + "ActorClass": "NoActor", "DisplayName": "Second boost element", "ShNodeId": "ad51bb8f-f1bb-4a16-8652-76ed699c6a19", "ComponentId": "d5fbf9f4-18a5-48f8-abdf-919309424321", @@ -86,8 +86,8 @@ }, { "Alias": "a.elt2.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BooleanActuator", + "ActorClass": "NoActor", "DisplayName": "30A Relay for second boost element", "ShNodeId": "d2eb5c93-ba1c-4f2c-a7ad-4dee995eb811", "ComponentId": "58c4ad4f-6b1a-4fbf-80dd-d269f31e5ba5", @@ -96,8 +96,8 @@ }, { "Alias": "a.tank", - "RoleGtEnumSymbol": "3ecfe9b8", - "ActorClassGtEnumSymbol": "00000000", + "Role": "DedicatedThermalStore", + "ActorClass": "NoActor", "DisplayName": "Little Orange House Test Axeman Tank", "ShNodeId": "e9cbe9bf-7129-4508-b4d9-90d3af51b013", "ComponentId": "d4a27899-98a3-409f-8c6a-771d86d7937b", @@ -106,8 +106,8 @@ }, { "Alias": "a.m", - "RoleGtEnumSymbol": "9ac68b6e", - "ActorClassGtEnumSymbol": "2ea112b9", + "Role": "PowerMeter", + "ActorClass": "PowerMeter", "DisplayName": "Main Power Meter Little Orange House Test System", "ShNodeId": "0dd8a803-4724-4f49-b845-14ff57bdb3e6", "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", @@ -116,8 +116,8 @@ }, { "Alias": "a.tank.out", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", + "Role": "HydronicPipe", + "ActorClass": "NoActor", "DisplayName": "Main Heat Distribution Pipe Out of Tank", "ShNodeId": "192a38da-fb2e-4233-a513-dc837f26e7f9", "ComponentId": "1d62c7ce-b484-4610-a8b8-3979c8402588", @@ -126,8 +126,8 @@ }, { "Alias": "a.tank.in", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", + "Role": "HydronicPipe", + "ActorClass": "NoActor", "DisplayName": "Main Heat Distribution Pipe into Tank", "ShNodeId": "c9a80dca-8f60-4772-96f3-53793e64ef8a", "ComponentId": "f4341350-973a-403a-98fe-dfd2a4acf038", @@ -136,8 +136,8 @@ }, { "Alias": "a.tank.out.pump", - "RoleGtEnumSymbol": "b0eaf2ba", - "ActorClassGtEnumSymbol": "00000000", + "Role": "CirculatorPump", + "ActorClass": "NoActor", "DisplayName": "Circulator Pump", "ShNodeId": "f3db8728-1751-4f30-a739-d56bbff9971e", "ComponentId": "1c207b38-d7bf-41cd-a9ad-6204bbab9922", @@ -146,8 +146,8 @@ }, { "Alias": "a.tank.out.pump.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BooleanActuator", + "ActorClass": "NoActor", "DisplayName": "Circulator Pump relay", "ShNodeId": "357a45f0-1a57-4f3d-9be4-4ff77cf19ba2", "ComponentId": "e758eecd-6439-4e1d-9f66-534f6fc14859", @@ -156,8 +156,8 @@ }, { "Alias": "a.tank.out.pump.allradiators", - "RoleGtEnumSymbol": "05fdd645", - "ActorClassGtEnumSymbol": "00000000", + "Role": "BaseboardRadiator", + "ActorClass": "NoActor", "DisplayName": "All the garage passive radiators", "ShNodeId": "7d555c14-dcfe-43ba-9f50-92bd174c1d17", "TypeName": "spaceheat.node.gt", @@ -165,8 +165,8 @@ }, { "Alias": "a.tank.out.pump.allradiators.garage", - "RoleGtEnumSymbol": "65725f44", - "ActorClassGtEnumSymbol": "00000000", + "Role": "HeatedSpace", + "ActorClass": "NoActor", "DisplayName": "Little Orange House Garage", "ShNodeId": "9db24e3b-10bf-4d8a-bb30-221230a90870", "ComponentId": "f3a8c03b-ae00-4124-9c46-d98961636ddc", @@ -175,8 +175,8 @@ }, { "Alias": "a.outdoors", - "RoleGtEnumSymbol": "dd975b31", - "ActorClassGtEnumSymbol": "00000000", + "Role": "Outdoors", + "ActorClass": "NoActor", "DisplayName": "43 Avon St Microclimate", "ShNodeId": "4dec0102-41b6-4255-ade6-c5b55dc16f01", "TypeName": "spaceheat.node.gt", @@ -184,8 +184,8 @@ }, { "Alias": "a.tank.out.flowmeter1", - "RoleGtEnumSymbol": "ece3b600", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "PipeFlowMeter", + "ActorClass": "SimpleSensor", "DisplayName": "Flow Meter on distribution pipe out of tank", "ShNodeId": "fcc8cc94-75eb-42e1-8ef1-024961b053b7", "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", @@ -195,8 +195,8 @@ }, { "Alias": "a.tank.out.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "PipeTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Temp Sensor on distribution pipe out of tank", "ShNodeId": "66a4a5e7-f160-424a-8ad5-f6554bf9a99a", "ComponentId": "a16d7bb6-2606-4ad1-b6b4-be80b5d84a6e", @@ -206,8 +206,8 @@ }, { "Alias": "a.tank.in.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "PipeTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Temp Sensor on distribution pipe into tank", "ShNodeId": "d9c461f6-ed11-4dc1-9fe2-992d5d2413d0", "ComponentId": "efabaa6a-e331-491f-9dbb-a3dee0029531", @@ -217,8 +217,8 @@ }, { "Alias": "a.tank.temp0", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "TankWaterTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Tank temp sensor temp0 (on top)", "ShNodeId": "3593a10a-4335-447a-b62e-e123788a134a", "ComponentId": "f516467e-7691-42c8-8525-f7d49bb135ce", @@ -228,8 +228,8 @@ }, { "Alias": "a.tank.temp1", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "TankWaterTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Tank temp sensor temp1 (second from top)", "ShNodeId": "0df93059-f155-4c4f-a43c-0265a28f21fb", "ComponentId": "d8d9f2b6-db8b-4a23-a037-20c7308ec56e", @@ -239,8 +239,8 @@ }, { "Alias": "a.tank.temp2", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "TankWaterTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Tank temp sensor temp2 (third from top)", "ShNodeId": "17f84be9-6237-4331-91f8-17452eba0345", "ComponentId": "5453b43b-7940-463c-ae6e-5cfe86ecbd3d", @@ -250,8 +250,8 @@ }, { "Alias": "a.tank.temp3", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "TankWaterTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Tank temp sensor temp3 (fourth from top)", "ShNodeId": "296c9002-5771-4e11-969c-48a6db905b83", "ComponentId": "17110718-b102-4c3b-ae62-5331aec9eed3", @@ -261,8 +261,8 @@ }, { "Alias": "a.tank.temp4", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "TankWaterTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "Tank temp sensor temp4 (fifth from top)", "ShNodeId": "0680a809-d56f-42d8-8d98-e7c23e94d311", "ComponentId": "bc2b731d-2b68-4cb8-9c27-8dd84dd2ea82", @@ -272,8 +272,8 @@ }, { "Alias": "a.tank.out.pump.allradiators.garage", - "RoleGtEnumSymbol": "65725f44", - "ActorClassGtEnumSymbol": "00000000", + "Role": "HeatedSpace", + "ActorClass": "NoActor", "DisplayName": "Little Orange House Garage", "ShNodeId": "26ed27a5-f362-4044-8bd7-19ce8acf4d63", "TypeName": "spaceheat.node.gt", @@ -281,8 +281,8 @@ }, { "Alias": "a.tank.out.pump.allradiators.garage.temp1", - "RoleGtEnumSymbol": "fec74958", - "ActorClassGtEnumSymbol": "dae4b2f0", + "Role": "RoomTempSensor", + "ActorClass": "SimpleSensor", "DisplayName": "First Garage Temp sensor", "ShNodeId": "2793ae37-e680-404e-a4ad-1efb0b8987cf", "ComponentId": "23a09f17-741a-49b2-afbc-04f17f476594", @@ -292,8 +292,8 @@ }, { "Alias": "a.outdoors.temp1", - "RoleGtEnumSymbol": "5938bf1f", - "ActorClassGtEnumSymbol": "00000000", + "Role": "5938bf1f", + "ActorClass": "NoActor", "DisplayName": "First Outdoor Temp sensor", "ShNodeId": "4c94cf37-dd87-49de-b782-aac97b69c154", "ComponentId": "863359e1-f30a-4090-90a6-fc93154494a1", @@ -302,40 +302,6 @@ "Version": "100" } ], - "ThermalEdges": [ - { - "FromNodeAlias": "a.elt1", - "ToNodeAlias": "a.tank" - }, - { - "FromNodeAlias": "a.tank.in", - "ToNodeAlias": "a.tank" - }, - { - "FromNodeAlias": "a.tank", - "ToNodeAlias": "a.tank.out" - }, - { - "FromNodeAlias": "a.tank.out", - "ToNodeAlias": "a.tank.out.pump" - }, - { - "FromNodeAlias": "a.tank.out.pump", - "ToNodeAlias": "a.tank.out.pump.allradiators" - }, - { - "FromNodeAlias": "a.tank.out.pump.allradiators", - "ToNodeAlias": "a.tank.in" - }, - { - "FromNodeAlias": "a.tank.out.pump.allradiators", - "ToNodeAlias": "a.tank.out.pump.allradiators.garage" - }, - { - "FromNodeAlias": "a.tank.out.pump.allradiators.garage", - "ToNodeAlias": "a.outdoors" - } - ], "ResistiveHeaterComponents": [ { "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", @@ -473,8 +439,8 @@ "NameplateMaxValue": 4500, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3" + "TelemetryName": "Power", + "Unit": "W" }, { "AboutNodeName": "a.elt1", @@ -485,8 +451,8 @@ "NameplateMaxValue": 18750000, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "ad19e79c", - "UnitGtEnumSymbol": "a969ac7c" + "TelemetryName": "CurrentRmsMicroAmps", + "Unit": "AmpsRms" }, { "AboutNodeName": "a.elt2", @@ -497,8 +463,8 @@ "NameplateMaxValue": 4500, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3" + "TelemetryName": "Power", + "Unit": "W" } ], "HwUid": "1001ab", @@ -523,27 +489,32 @@ { "ComponentId": "d4a27899-98a3-409f-8c6a-771d86d7937b", "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407" + "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", + "TypeName": "component.gt" }, { "ComponentId": "1d62c7ce-b484-4610-a8b8-3979c8402588", "DisplayName": "Hydronic pipe out of tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa" + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + "TypeName": "component.gt" }, { "ComponentId": "f4341350-973a-403a-98fe-dfd2a4acf038", "DisplayName": "Hydronic pipe into tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa" + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", + "TypeName": "component.gt" }, { "ComponentId": "1c207b38-d7bf-41cd-a9ad-6204bbab9922", "DisplayName": "Circulator Pump", - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776" + "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", + "TypeName": "component.gt" }, { "ComponentId": "f3a8c03b-ae00-4124-9c46-d98961636ddc", "DisplayName": "Little Orange House garage", - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d" + "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", + "TypeName": "component.gt" } ], "ResistiveHeaterCacs": [ @@ -587,9 +558,33 @@ "TelemetryNameList": ["PowerW"], "TypeName": "electric.meter.cac.gt", "Version": "000" + }, + { + "ComponentAttributeClassId": "204832ef-0c88-408b-9640-264d2ee74914", + "MakeModel": "GRIDWORKS__SIMPM1", + "DisplayName": "Gridworks Pm1 Simulated Power Meter # 2", + "Interface": "SIMRABBIT", + "PollPeriodMs": 1000, + "TelemetryNameList": ["PowerW"], + "TypeName": "electric.meter.cac.gt", + "Version": "000" } ], - "MultipurposeSensorCacs": [], + "MultipurposeSensorCacs": [ + { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "MakeModel": "GRIDWORKS__MULTITEMP1", + "PollPeriodMs": 880, + "Exponent": -3, + "TempUnit": "Celcius", + "TelemetryNameList": ["WaterTempCTimes1000"], + "MaxThermistors": 12, + "DisplayName": "Simulated GridWorks high precision water temp sensor", + "CommsMethod": "I2C", + "TypeName": "multipurpose.sensor.cac.gt", + "Version": "000" + } + ], "SimpleTempSensorCacs": [ { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", diff --git a/tests/conftest.py b/tests/conftest.py index b044e44b..e69de29b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +0,0 @@ -"""Local pytest configuration""" - -import pytest - -from tests.utils import flush_all - - -@pytest.fixture(autouse=True) -def flush_local_registries() -> None: - flush_all() diff --git a/tests/data_classes/test_electric_meter_component.py b/tests/data_classes/test_electric_meter_component.py deleted file mode 100644 index 8e363020..00000000 --- a/tests/data_classes/test_electric_meter_component.py +++ /dev/null @@ -1,56 +0,0 @@ -from gwproto.data_classes.components.electric_meter_component import ( - ElectricMeterComponent, -) -from gwproto.data_classes.hardware_layout import HardwareLayout -from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker - -# Running the below disrupts other tests. Need to set up the -# test isolation as per scada - - -def test_electric_meter_component() -> None: - errors = [] - HardwareLayout.load( - "tests/config/hardware-layout.json", errors=errors, raise_errors=False - ) - d = { - "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", - "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "DisplayName": "Power Meter for Simulated Test system", - "ConfigList": [ - { - "AboutNodeName": "a.elt1", - "ReportOnChange": True, - "SamplePeriodS": 300, - "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3", - }, - { - "AboutNodeName": "a.elt2", - "ReportOnChange": True, - "SamplePeriodS": 300, - "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3", - }, - ], - "HwUid": "9999", - "EgaugeIoList": [], - "TypeName": "electric.meter.component.gt", - "Version": "000", - } - - gw_tuple = ElectricMeterComponentGt_Maker.dict_to_tuple(d) - assert gw_tuple.ComponentId in ElectricMeterComponent.by_id - component_as_dc = ElectricMeterComponent.by_id[gw_tuple.ComponentId] - assert gw_tuple.HwUid == "9999" - assert component_as_dc.hw_uid == "1001ab" diff --git a/tests/data_classes/test_hardware_layout.py b/tests/data_classes/test_hardware_layout.py index 0506b297..d4924196 100644 --- a/tests/data_classes/test_hardware_layout.py +++ b/tests/data_classes/test_hardware_layout.py @@ -2,8 +2,4 @@ def test_hardware_layout() -> None: - errors = [] - HardwareLayout.load( - "tests/config/hardware-layout.json", errors=errors, raise_errors=False - ) - assert not errors + HardwareLayout.load("tests/config/hardware-layout.json") diff --git a/tests/test_misc/__init__.py b/tests/test_misc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/test_misc/test_flush_and_load_house.py b/tests/test_misc/test_flush_and_load_house.py deleted file mode 100644 index 7726c584..00000000 --- a/tests/test_misc/test_flush_and_load_house.py +++ /dev/null @@ -1,535 +0,0 @@ -"""Test load_house module""" - -from gwproto.data_classes.hardware_layout import HardwareLayout -from gwproto.data_classes.sh_node import ShNode -from gwproto.types import SpaceheatNodeGt_Maker -from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt_Maker -from tests.utils import flush_all - - -def test_flush_and_load_house() -> None: - """Verify that flush_house() successfully removes all dictionary data from relevant dataclasses, and - load_house() successfully loads test objects""" - flush_all() - - electric_meter_component_dict = { - "ComponentId": "c7d352db-9a86-40f0-9601-d99243719cc5", - "DisplayName": "Test unknown meter", - "ComponentAttributeClassId": "c1f17330-6269-4bc5-aa4b-82e939e9b70c", - "HwUid": "7ec4a224", - "EgaugeIoList": [], - "ConfigList": [], - "TypeName": "electric.meter.component.gt", - "Version": "001", - } - - meter_node_dict = { - "Alias": "a.m", - "RoleGtEnumSymbol": "9ac68b6e", - "ActorClassGtEnumSymbol": "2ea112b9", - "DisplayName": "Main Power Meter Little Orange House Test System", - "ShNodeId": "c9456f5b-5a39-4a48-bb91-742a9fdc461d", - "ComponentId": "c7d352db-9a86-40f0-9601-d99243719cc5", - "TypeName": "spaceheat.node.gt", - "Version": "100", - } - - ElectricMeterComponentGt_Maker.dict_to_dc(electric_meter_component_dict) - SpaceheatNodeGt_Maker.dict_to_dc(meter_node_dict) - assert ShNode.by_id["c9456f5b-5a39-4a48-bb91-742a9fdc461d"].alias == "a.m" - flush_all() - - -def test_load_real_house() -> None: - layout = HardwareLayout( - { - "MyAtomicTNodeGNode": { - "GNodeId": "d636cbeb-c4ad-45cd-b5bc-1c64cc33f4f4", - "Alias": "w.isone.ct.newhaven.orange1", - "DisplayName": "Little Orange House Garage Heating System AtomicTNode", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "AtomicTNode", - }, - "MyTerminalAssetGNode": { - "GNodeId": "137d7f06-ea65-4254-bfd1-5d56fa789229", - "Alias": "w.isone.ct.newhaven.orange1.ta", - "DisplayName": "Little Orange House Garage Heating System TerminalAsset", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "TerminalAsset", - }, - "MyScadaGNode": { - "GNodeId": "28817671-3899-4e24-a337-abcb8633e47a", - "Alias": "w.isone.ct.newhaven.orange1.scada", - "DisplayName": "Little Orange House Garage Heating System SCADA", - "GNodeStatusValue": "Active", - "PrimaryGNodeRoleAlias": "Scada", - }, - "ShNodes": [ - { - "Alias": "a", - "RoleGtEnumSymbol": "6ddff83b", - "ActorClassGtEnumSymbol": "b103058f", - "DisplayName": "AtomicTNode", - "ShNodeId": "7a4fe194-f572-407e-ab65-8d38f83d9eb0", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.home", - "RoleGtEnumSymbol": "863e50d1", - "ActorClassGtEnumSymbol": "32d3d19f", - "DisplayName": "Little Orange House HomeAlone", - "ShNodeId": "a1537de0-5c83-422e-9826-0995e0419953", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.s", - "RoleGtEnumSymbol": "d0afb424", - "ActorClassGtEnumSymbol": "6d37aa41", - "DisplayName": "Little Orange House Main Scada", - "ShNodeId": "19fc828e-0f9f-4a15-819b-6f02e38500c7", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1", - "RoleGtEnumSymbol": "99c5f326", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First 4.5 kW boost in tank", - "ShNodeId": "164d6c73-7c0c-4063-8cf9-01cde3a32b7c", - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "RatedVoltageV": 240, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.elt1.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "30A Relay for first boost element", - "ShNodeId": "a9c94a2f-1800-4394-a90f-4f50dba053ac", - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank", - "RoleGtEnumSymbol": "3ecfe9b8", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Test Axeman Tank", - "ShNodeId": "4ac89701-3472-4fb2-b404-8e3012af0399", - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.m", - "RoleGtEnumSymbol": "9ac68b6e", - "ActorClassGtEnumSymbol": "2ea112b9", - "DisplayName": "Main Power Meter Little Orange House Test System", - "ShNodeId": "55cecbdb-8a47-4160-a1d6-f3617a6279b4", - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe Out of Tank", - "ShNodeId": "1348b9cc-d45e-4095-aa39-24abb58a7498", - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in", - "RoleGtEnumSymbol": "fe3cbdd5", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Main Heat Distribution Pipe into Tank", - "ShNodeId": "2aeb26f7-8251-431c-83b5-0ad3c0e4cff0", - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump", - "RoleGtEnumSymbol": "b0eaf2ba", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Circulator Pump", - "ShNodeId": "4dcf1be8-35ea-48c5-a7a0-9d74476b5a8d", - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Circulator Pump Relay", - "ShNodeId": "d6109c64-daec-49b1-8b4f-1c69ab012e69", - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1", - "RoleGtEnumSymbol": "05fdd645", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Single baseboard radiator", - "ShNodeId": "9d8641f7-f5e2-4683-a427-5ebb18345b89", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan", - "RoleGtEnumSymbol": "6896109b", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First baseboard radiator fan", - "ShNodeId": "57b3632f-df3e-4a7f-9205-1e6f953dece9", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.fan.relay", - "RoleGtEnumSymbol": "57b788ee", - "ActorClassGtEnumSymbol": "fddd0064", - "DisplayName": "Relay for first baseboard radiator fan", - "ShNodeId": "0223a903-ae99-4cd9-bd67-f28e1e799938", - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage", - "RoleGtEnumSymbol": "65725f44", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "Little Orange House Garage", - "ShNodeId": "e06809dc-7915-4389-9559-d4a89c3c4994", - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors", - "RoleGtEnumSymbol": "dd975b31", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "43 Avon St Microclimate", - "ShNodeId": "7706f7ae-d01e-4ec2-89f4-8eb6fcc64f18", - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.flowmeter1", - "RoleGtEnumSymbol": "ece3b600", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Flow Meter on distribution pipe out of tank", - "ShNodeId": "0cb98277-b4b5-4016-8afa-ed2bffca6750", - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe out of tank", - "ShNodeId": "88b30858-65c0-470b-82e5-0981d3f2b5fe", - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.in.temp1", - "RoleGtEnumSymbol": "c480f612", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Temp Sensor on distribution pipe into tank", - "ShNodeId": "675458cf-2da8-4792-8c50-e04c3f2f8326", - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.temp0", - "RoleGtEnumSymbol": "73308a1f", - "ActorClassGtEnumSymbol": "dae4b2f0", - "DisplayName": "Tank temp sensor temp0 (on top)", - "ShNodeId": "24e6c994-ff61-43b2-8550-d2017f413cb3", - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.tank.out.pump.baseboard1.garage.temp1", - "RoleGtEnumSymbol": "fec74958", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Garage Temp sensor", - "ShNodeId": "12b072ef-0a8b-4569-a055-12b486f35f04", - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - { - "Alias": "a.outdoors.temp1", - "RoleGtEnumSymbol": "5938bf1f", - "ActorClassGtEnumSymbol": "00000000", - "DisplayName": "First Outdoor Temp sensor", - "ShNodeId": "f0821873-e34f-4d53-a69a-10f6bed6d8d8", - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100", - }, - ], - "ThermalEdges": [ - {"FromNodeAlias": "a.elt1", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank.in", "ToNodeAlias": "a.tank"}, - {"FromNodeAlias": "a.tank", "ToNodeAlias": "a.tank.out"}, - {"FromNodeAlias": "a.tank.out", "ToNodeAlias": "a.tank.out.pump"}, - { - "FromNodeAlias": "a.tank.out.pump", - "ToNodeAlias": "a.tank.out.pump.baseboard1", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.in", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1", - "ToNodeAlias": "a.tank.out.pump.baseboard1.garage", - }, - { - "FromNodeAlias": "a.tank.out.pump.baseboard1.garage", - "ToNodeAlias": "a.outdoors", - }, - ], - "ResistiveHeaterComponents": [ - { - "ComponentId": "26856174-e2b2-44f3-9f48-048201f1c0e8", - "DisplayName": "First 4.5 kW boost in tank", - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "TypeName": "resistive.heater.component.gt", - "Version": "000", - "MaxPowerW": 4500, - } - ], - "RelayComponents": [ - { - "ComponentId": "dd9a1452-d7aa-4523-8deb-8e302a4f86ba", - "DisplayName": "relay for radiator fan", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 1, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "57e2eb41-08f4-4032-8948-b14890fce9ca", - "DisplayName": "relay for first elt in tank", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 2, - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - }, - { - "ComponentId": "7de91fae-72ab-4226-8ebe-f66a9d85cea4", - "DisplayName": "relay for main circulator pump", - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "Gpio": 3, - "NormallyOpen": False, - "TypeName": "relay.component.gt", - "Version": "000", - }, - ], - "MultipurposeSensorComponents": [], - "SimpleTempSensorComponents": [ - { - "ComponentId": "a9d43bb7-f838-4b7c-89ed-186eb8c89f23", - "DisplayName": "Outdoor Temperature Sensor", - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "DisplayName": "Temp sensor on pipe out of tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00033ffe", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "35b5107c-bf32-4791-93eb-0497929fae57", - "DisplayName": "Temp sensor on pipe into tank", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "000363a9", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "2d4b3b73-fc58-4789-b15e-9881f0b4ff40", - "DisplayName": "Component for a.tank.temp0 (on top)", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "HwUid": "00041d3f", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - { - "ComponentId": "38d2db8d-3668-4479-a839-c4b0298be270", - "DisplayName": "First garage temp sensor", - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - }, - ], - "ElectricMeterComponents": [ - { - "ComponentId": "04ceb282-d7e8-4293-80b5-72455e1a5db3", - "DisplayName": "Main power meter for Little orange house garage space heat", - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "HwUid": "1001ab", - "TypeName": "electric.meter.component.gt", - "Version": "000", - } - ], - "PipeFlowSensorComponents": [ - { - "ComponentId": "cec2fe5c-977c-4e5f-b299-f70adbc38523", - "DisplayName": "Flow meter on pipe out of tank", - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "TypeName": "pipe.flow.sensor.component.gt", - "Version": "000", - } - ], - "OtherComponents": [ - { - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - }, - { - "ComponentId": "71a224e5-8fa6-4af5-b4d0-ddbae4ca8b81", - "DisplayName": "Hydronic pipe out of tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "cdaa1c34-96d0-4713-9b89-c5b80b2668e6", - "DisplayName": "Hydronic pipe into tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - }, - { - "ComponentId": "5cbf2fd0-2987-42c6-99bf-2eaf1a17060b", - "DisplayName": "Circulator Pump", - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - }, - { - "ComponentId": "7b1e4102-7fb5-4048-93ae-312a12d47ba8", - "DisplayName": "Little Orange House garage", - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - }, - ], - "ResistiveHeaterCacs": [ - { - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModelGtEnumSymbol": "00000000", - "DisplayName": "Orange Garage heating element", - "NameplateMaxPowerW": 4500, - "RatedVoltageV": 240, - "TypeName": "resistive.heater.cac.gt", - "Version": "000", - } - ], - "RelayCacs": [ - { - "ComponentAttributeClassId": "c6e736d8-8078-44f5-98bb-d72ca91dc773", - "MakeModelGtEnumSymbol": "fabfa505", - "TypicalResponseTimeMs": 200, - "TypeName": "relay.cac.gt", - "Version": "000", - } - ], - "PipeFlowSensorCacs": [ - { - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModelGtEnumSymbol": "00000000", - "TypeName": "pipe.flow.sensor.cac.gt", - "Version": "000", - } - ], - "ElectricMeterCacs": [ - { - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - "MakeModelGtEnumSymbol": "d300635e", - "DisplayName": "Schneider Electric Iem3455 Power Meter", - "InterfaceGtEnumSymbol": "a6a4ac9f", - "PollPeriodMs": 1000, - "DefaultBaud": 9600, - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - ], - "MultipurposeSensorCacs": [], - "SimpleTempSensorCacs": [ - { - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", - "MakeModelGtEnumSymbol": "acd93fb3", - "DisplayName": "Adafruit High Temp Waterproof DS18B20 Digital Temp Sensor", - "CommsMethod": "OneWire", - "Exponent": -3, - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TempUnitGtEnumSymbol": "ec14bd47", - "TypicalResponseTimeMs": 880, - "TypeName": "simple.temp.sensor.cac.gt", - "Versoin": "000", - }, - { - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - { - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "MakeModelGtEnumSymbol": "00000000", - "TempUnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - }, - ], - "OtherCacs": [ - { - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "MakeModelGtEnumSymbol": "00000000", - }, - { - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "MakeModelGtEnumSymbol": "00000000", - }, - ], - } - ) - for node in layout.nodes.values(): - layout.parent_node(node.alias) diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index 97aeee1b..2f9fa3ad 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -1,105 +1,17 @@ """Tests component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import ComponentGt_Maker as Maker +from gwproto.data_classes.components import Component +from gwproto.types import ComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_component_gt_generated() -> None: d = { "ComponentId": "987e0a5f-9036-411e-ba30-bac1075114ba", - "ComponentAttributeClassId": "0a2fed00-8ff9-4391-a6d8-4b08ab94dfe1", + "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", "DisplayName": "Sample Component", "HwUid": "000aaa", "TypeName": "component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - hw_uid=gtuple.HwUid, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load([ComponentCase("ComponentGt", d, ComponentGt, Component)]) diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py index 7b34cc3f..47bc0df1 100644 --- a/tests/types/test_egauge_io.py +++ b/tests/types/test_egauge_io.py @@ -1,12 +1,6 @@ """Tests egauge.io type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import EgaugeIo_Maker as Maker +from gwproto.types import EgaugeIo def test_egauge_io_generated() -> None: @@ -30,60 +24,10 @@ def test_egauge_io_generated() -> None: "NameplateMaxValue": 4500, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3", + "TelemetryName": "PowerW", + "Unit": "W", }, "TypeName": "egauge.io", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - input_config=gtuple.InputConfig, - output_config=gtuple.OutputConfig, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["InputConfig"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["OutputConfig"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) + assert EgaugeIo.model_validate(d).model_dump() == d diff --git a/tests/types/test_egauge_register_config.py b/tests/types/test_egauge_register_config.py index 39237ccb..fae65b0b 100644 --- a/tests/types/test_egauge_register_config.py +++ b/tests/types/test_egauge_register_config.py @@ -1,12 +1,6 @@ """Tests egauge.register.config type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import EgaugeRegisterConfig_Maker as Maker +from gwproto.types import EgaugeRegisterConfig def test_egauge_register_config_generated() -> None: @@ -20,86 +14,4 @@ def test_egauge_register_config_generated() -> None: "TypeName": "egauge.register.config", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - address=gtuple.Address, - name=gtuple.Name, - description=gtuple.Description, - type=gtuple.Type, - denominator=gtuple.Denominator, - unit=gtuple.Unit, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Address"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Name"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Description"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Type"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Denominator"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Unit"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, Address="9004.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Denominator="1.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) + assert EgaugeRegisterConfig.model_validate(d).model_dump() == d diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 397c0cf2..e45b6eeb 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -1,14 +1,8 @@ """Tests electric.meter.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types.electric_meter_component_gt import ( - ElectricMeterComponentGt_Maker as Maker, -) +from gwproto.data_classes.components import ElectricMeterComponent +from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_electric_meter_component_gt_generated() -> None: @@ -26,8 +20,8 @@ def test_electric_meter_component_gt_generated() -> None: "NameplateMaxValue": 3500, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3", + "TelemetryName": "PowerW", + "Unit": "W", } ], "HwUid": "BP01349", @@ -54,8 +48,8 @@ def test_electric_meter_component_gt_generated() -> None: "NameplateMaxValue": 3500, "TypeName": "telemetry.reporting.config", "Version": "000", - "TelemetryNameGtEnumSymbol": "af39eec9", - "UnitGtEnumSymbol": "f459a9c3", + "TelemetryName": "PowerW", + "Unit": "W", }, "TypeName": "egauge.io", "Version": "000", @@ -64,152 +58,13 @@ def test_electric_meter_component_gt_generated() -> None: "TypeName": "electric.meter.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - config_list=gtuple.ConfigList, - hw_uid=gtuple.HwUid, - modbus_host=gtuple.ModbusHost, - modbus_port=gtuple.ModbusPort, - egauge_io_list=gtuple.EgaugeIoList, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ConfigList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["EgaugeIoList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - # Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist! - del d2["ModbusHost"] - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2["ConfigList"] = [] - # Axiom 2 part 2: If EgaugeIoList has non-zero length then it has the same length as ConfigList - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2["EgaugeIoList"] = [] - Maker.dict_to_tuple(d2) - - if "ModbusPort" in d2: - del d2["ModbusPort"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ConfigList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ConfigList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ConfigList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ModbusPort="502.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, EgaugeIoList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, EgaugeIoList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, EgaugeIoList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ModbusPort=-1) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ + ComponentCase( + "ElectricMeterComponentGt", + d, + ElectricMeterComponentGt, + ElectricMeterComponent, + ) + ] + ) diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index bac4ff6a..2b404153 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -7,13 +7,10 @@ def test_multipurpose_sensor_cac_gt_load() -> None: d = { "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - # "MakeModelGtEnumSymbol": "09185ae3", "MakeModel": "GRIDWORKS__MULTITEMP1", "PollPeriodMs": 880, "Exponent": -3, - # "TempUnitGtEnumSymbol": "8e6dd6dd", "TempUnit": "Celcius", - # "TelemetryNameList": ["22641963"], "TelemetryNameList": ["WaterTempCTimes1000"], "MaxThermistors": 12, "DisplayName": "Simulated GridWorks high precision water temp sensor", diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index 92308e0f..66898a1f 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -1,14 +1,8 @@ """Tests multipurpose.sensor.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types.multipurpose_sensor_component_gt import ( - MultipurposeSensorComponentGt_Maker as Maker, -) +from gwproto.data_classes.components import MultipurposeSensorComponent +from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_multipurpose_sensor_component_gt_generated() -> None: @@ -24,8 +18,8 @@ def test_multipurpose_sensor_component_gt_generated() -> None: "SamplePeriodS": 60, "TypeName": "telemetry.reporting.config", "Version": "000", - "UnitGtEnumSymbol": "ec14bd47", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "Unit": "Celcius", + "TelemetryName": "WaterTempCTimes1000", } ], "HwUid": "a4f", @@ -33,112 +27,13 @@ def test_multipurpose_sensor_component_gt_generated() -> None: "TypeName": "multipurpose.sensor.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - channel_list=gtuple.ChannelList, - config_list=gtuple.ConfigList, - hw_uid=gtuple.HwUid, - display_name=gtuple.DisplayName, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ChannelList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ConfigList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ConfigList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ConfigList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ConfigList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ + ComponentCase( + "MultipurposeSensorComponentGt", + d, + MultipurposeSensorComponentGt, + MultipurposeSensorComponent, + ) + ] + ) diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py index 258ef71c..71e98e84 100644 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ b/tests/types/test_pipe_flow_sensor_component_gt.py @@ -1,18 +1,14 @@ """Tests pipe.flow.sensor.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import PipeFlowSensorComponentGt_Maker as Maker +from gwproto.data_classes.components import PipeFlowSensorComponent +from gwproto.types import PipeFlowSensorComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_pipe_flow_sensor_component_gt_generated() -> None: d = { "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", - "ComponentAttributeClassId": "13d916dc-8764-4b16-b85d-b8ead3e2fc80", + "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", "I2cAddress": 100, "ConversionFactor": 0.1328, "DisplayName": "Flow meter on pipe out of tank", @@ -21,118 +17,13 @@ def test_pipe_flow_sensor_component_gt_generated() -> None: "TypeName": "pipe.flow.sensor.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - i2c_address=gtuple.I2cAddress, - conversion_factor=gtuple.ConversionFactor, - display_name=gtuple.DisplayName, - hw_uid=gtuple.HwUid, - expected_max_gpm_times100=gtuple.ExpectedMaxGpmTimes100, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["I2cAddress"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ConversionFactor"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "ExpectedMaxGpmTimes100" in d2: - del d2["ExpectedMaxGpmTimes100"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, I2cAddress="100.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ConversionFactor="this is not a float") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ExpectedMaxGpmTimes100="1000.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ + ComponentCase( + "PipeFlowSensorComponentGt", + d, + PipeFlowSensorComponentGt, + PipeFlowSensorComponent, + ) + ] + ) diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py index 0e469f3e..8bf5f5ab 100644 --- a/tests/types/test_relay_component_gt.py +++ b/tests/types/test_relay_component_gt.py @@ -1,12 +1,8 @@ """Tests relay.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import RelayComponentGt_Maker as Maker +from gwproto.data_classes.components import RelayComponent +from gwproto.types import RelayComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_relay_component_gt_generated() -> None: @@ -20,108 +16,6 @@ def test_relay_component_gt_generated() -> None: "TypeName": "relay.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - gpio=gtuple.Gpio, - hw_uid=gtuple.HwUid, - normally_open=gtuple.NormallyOpen, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["NormallyOpen"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "Gpio" in d2: - del d2["Gpio"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, Gpio="0.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, NormallyOpen="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ComponentCase("RelayComponentGt", d, RelayComponentGt, RelayComponent)], + ) diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index fc968072..f2db762c 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -1,12 +1,8 @@ """Tests resistive.heater.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import ResistiveHeaterComponentGt_Maker as Maker +from gwproto.data_classes.components import ResistiveHeaterComponent +from gwproto.types import ResistiveHeaterComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_resistive_heater_component_gt_generated() -> None: @@ -20,108 +16,13 @@ def test_resistive_heater_component_gt_generated() -> None: "TypeName": "resistive.heater.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - hw_uid=gtuple.HwUid, - tested_max_hot_milli_ohms=gtuple.TestedMaxHotMilliOhms, - tested_max_cold_milli_ohms=gtuple.TestedMaxColdMilliOhms, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "TestedMaxHotMilliOhms" in d2: - del d2["TestedMaxHotMilliOhms"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "TestedMaxColdMilliOhms" in d2: - del d2["TestedMaxColdMilliOhms"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TestedMaxHotMilliOhms="13714.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TestedMaxColdMilliOhms="14500.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ + ComponentCase( + "ResistiveHeaterComponentGt", + d, + ResistiveHeaterComponentGt, + ResistiveHeaterComponent, + ) + ], + ) diff --git a/tests/types/test_simple_temp_sensor_component_gt.py b/tests/types/test_simple_temp_sensor_component_gt.py index e4125a43..adff3b8d 100644 --- a/tests/types/test_simple_temp_sensor_component_gt.py +++ b/tests/types/test_simple_temp_sensor_component_gt.py @@ -1,116 +1,27 @@ """Tests simple.temp.sensor.component.gt type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import SimpleTempSensorComponentGt_Maker as Maker +from gwproto.data_classes.components import SimpleTempSensorComponent +from gwproto.types import SimpleTempSensorComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_simple_temp_sensor_component_gt_generated() -> None: d = { "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ComponentAttributeClassId": "43564cd2-0e78-41a2-8b67-ad80c02161e8", + "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", "DisplayName": "Temp sensor on pipe out of tank", "HwUid": "00033ffe", "Channel": 0, "TypeName": "simple.temp.sensor.component.gt", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - component_id=gtuple.ComponentId, - component_attribute_class_id=gtuple.ComponentAttributeClassId, - display_name=gtuple.DisplayName, - hw_uid=gtuple.HwUid, - channel=gtuple.Channel, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ComponentAttributeClassId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "HwUid" in d2: - del d2["HwUid"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "Channel" in d2: - del d2["Channel"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, Channel="0.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ComponentId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert_component_load( + [ + ComponentCase( + "SimpleTempSensorComponentGt", + d, + SimpleTempSensorComponentGt, + SimpleTempSensorComponent, + ) + ], + ) diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 7e81d669..874be65e 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -1,182 +1,19 @@ """Tests spaceheat.node.gt type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import ActorClass, Role -from gwproto.errors import SchemaError -from gwproto.types import SpaceheatNodeGt_Maker as Maker +from gwproto.types import SpaceheatNodeGt def test_spaceheat_node_gt_generated() -> None: d = { "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", "Alias": "a.elt1", - "ActorClassGtEnumSymbol": "638bf97b", - "RoleGtEnumSymbol": "5a28eb2e", + "ActorClass": "NoActor", + "Role": "BoostElement", "DisplayName": "First boost element", "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", "ReportingSamplePeriodS": 300, - "RatedVoltageV": 240, - "TypicalVoltageV": 225, "InPowerMetering": False, "TypeName": "spaceheat.node.gt", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - sh_node_id=gtuple.ShNodeId, - alias=gtuple.Alias, - actor_class=gtuple.ActorClass, - role=gtuple.Role, - display_name=gtuple.DisplayName, - component_id=gtuple.ComponentId, - reporting_sample_period_s=gtuple.ReportingSamplePeriodS, - rated_voltage_v=gtuple.RatedVoltageV, - typical_voltage_v=gtuple.TypicalVoltageV, - in_power_metering=gtuple.InPowerMetering, - ).tuple - assert t == gtuple - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ShNodeId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Alias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ActorClassGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RoleGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - if "DisplayName" in d2: - del d2["DisplayName"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "ComponentId" in d2: - del d2["ComponentId"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "ReportingSamplePeriodS" in d2: - del d2["ReportingSamplePeriodS"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "RatedVoltageV" in d2: - del d2["RatedVoltageV"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "TypicalVoltageV" in d2: - del d2["TypicalVoltageV"] - Maker.dict_to_tuple(d2) - - d2 = dict(d) - if "InPowerMetering" in d2: - del d2["InPowerMetering"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ActorClassGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).ActorClass == ActorClass.default() - - d2 = dict(d, RoleGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).Role == Role.default() - - d2 = dict(d, ReportingSamplePeriodS="300.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, RatedVoltageV="240.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TypicalVoltageV="225.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, InPowerMetering="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ShNodeId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Alias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, RatedVoltageV=0) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TypicalVoltageV=0) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert SpaceheatNodeGt.model_validate(d).model_dump() == d diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index bcf9384c..e5620462 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -1,159 +1,19 @@ """Tests telemetry.reporting.config type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName, Unit -from gwproto.errors import SchemaError -from gwproto.types import TelemetryReportingConfig_Maker as Maker +from gwproto.types import TelemetryReportingConfig def test_telemetry_reporting_config_generated() -> None: d = { - "TelemetryNameGtEnumSymbol": "af39eec9", + "TelemetryName": "PowerW", "AboutNodeName": "a.elt1", "ReportOnChange": True, "SamplePeriodS": 300, "Exponent": 6, - "UnitGtEnumSymbol": "f459a9c3", + "Unit": "W", "AsyncReportThreshold": 0.2, "NameplateMaxValue": 4000, "TypeName": "telemetry.reporting.config", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - telemetry_name=gtuple.TelemetryName, - about_node_name=gtuple.AboutNodeName, - report_on_change=gtuple.ReportOnChange, - sample_period_s=gtuple.SamplePeriodS, - exponent=gtuple.Exponent, - unit=gtuple.Unit, - async_report_threshold=gtuple.AsyncReportThreshold, - nameplate_max_value=gtuple.NameplateMaxValue, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ReportOnChange"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SamplePeriodS"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Exponent"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["UnitGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Optional attributes can be removed from type - ###################################### - - d2 = dict(d) - del d2["AsyncReportThreshold"] - Maker.dict_to_tuple(d2) - - # Test axiom 1: If AsyncReportThreshold exists, NameplateMaxValue must as well - d2 = dict(d) - del d2["NameplateMaxValue"] - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - del d2["AsyncReportThreshold"] - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() - - d2 = dict(d, ReportOnChange="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SamplePeriodS="300.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Exponent="6.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, UnitGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).Unit == Unit.default() - - d2 = dict(d, AsyncReportThreshold="this is not a float") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, NameplateMaxValue="4000.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, AboutNodeName="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, NameplateMaxValue=0) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert TelemetryReportingConfig.model_validate(d).model_dump() == d diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py deleted file mode 100644 index 1191bec2..00000000 --- a/tests/utils/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -from gwproto.data_classes.component import Component -from gwproto.data_classes.components.electric_meter_component import ( - ElectricMeterComponent, -) -from gwproto.data_classes.components.multipurpose_sensor_component import ( - MultipurposeSensorComponent, -) -from gwproto.data_classes.components.pipe_flow_sensor_component import ( - PipeFlowSensorComponent, -) -from gwproto.data_classes.components.relay_component import RelayComponent -from gwproto.data_classes.components.resistive_heater_component import ( - ResistiveHeaterComponent, -) -from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorComponent, -) -from gwproto.data_classes.sh_node import ShNode - - -def flush_components() -> None: - RelayComponent.by_id = {} - ElectricMeterComponent.by_id = {} - PipeFlowSensorComponent.by_id = {} - MultipurposeSensorComponent.by_id = {} - ResistiveHeaterComponent.by_id = {} - SimpleTempSensorComponent.by_id = {} - Component.by_id = {} - - -def flush_spaceheat_nodes() -> None: - ShNode.by_id = {} - - -def flush_all() -> None: - flush_components() - flush_spaceheat_nodes() From 170ceacdc6ab7bd06fd1c74a8c79d8887b71b7d7 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 12 Sep 2024 13:54:52 -0400 Subject: [PATCH 103/168] HubitatComponentGt: stub hubitat now as valid UUID4 --- src/gwproto/types/hubitat_component_gt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index 2fa12bfb..b9e11ffc 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -30,7 +30,7 @@ def refresh_url(self, device_id: int) -> yarl.URL: def make_stub(cls, component_id: str) -> "HubitatComponentGt": return HubitatComponentGt( ComponentId=component_id, - ComponentAttributeClassId="00000000-0000-0000-0000-000000000000", + ComponentAttributeClassId="00000000-0000-4000-8000-000000000000", Hubitat=HubitatGt( Host="", MakerApiId=-1, From d839468bbbaa9d3fa80c434f892e4caa47215f81 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 12 Sep 2024 14:00:21 -0400 Subject: [PATCH 104/168] Component no longer a BaseModel, since BaseModel makes it tricky to write normal constructors --- src/gwproto/data_classes/components/component.py | 10 ++++++---- tests/component_load_utils.py | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gwproto/data_classes/components/component.py b/src/gwproto/data_classes/components/component.py index 4b03ac2e..3f6dbd63 100644 --- a/src/gwproto/data_classes/components/component.py +++ b/src/gwproto/data_classes/components/component.py @@ -2,20 +2,22 @@ from typing import Generic, TypeVar -from pydantic import BaseModel - from gwproto.types import ComponentAttributeClassGt, ComponentGt ComponentT = TypeVar("ComponentT", bound=ComponentGt) CacT = TypeVar("CacT", bound=ComponentAttributeClassGt) -class Component(BaseModel, Generic[ComponentT, CacT]): +class Component(Generic[ComponentT, CacT]): gt: ComponentT cac: CacT + def __init__(self, gt: ComponentT, cac: CacT) -> None: + self.gt = gt + self.cac = cac + def __repr__(self) -> str: return f"<{self.gt.DisplayName}> ({self.cac.MakeModel.value})" -class ComponentOnly(Component[ComponentGt, Component]): ... +class ComponentOnly(Component[ComponentGt, ComponentAttributeClassGt]): ... diff --git a/tests/component_load_utils.py b/tests/component_load_utils.py index e27a6d35..603244a1 100644 --- a/tests/component_load_utils.py +++ b/tests/component_load_utils.py @@ -114,10 +114,10 @@ def assert_component_load( exp_component_gt = case.exp_component_gt_type(**exp_component_gt) if case.exp_component is None: cac = cacs[exp_component_gt.ComponentAttributeClassId] - exp_component = case.exp_component_type(gt=exp_component_gt, cac=cac) + exp_component = case.exp_component_type(exp_component_gt, cac) else: exp_component = case.exp_component - if load_result.loaded != exp_component: + if load_result.loaded.__dict__ != exp_component.__dict__: errors.append( ComponentMatchError( case_idx=case_idx, From 4355d7c2a02ac7cd6f8dd3f81123f5ef9ee5154c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 12 Sep 2024 14:00:53 -0400 Subject: [PATCH 105/168] ShNode now has a Component object, filled in at construction --- src/gwproto/data_classes/hardware_layout.py | 33 ++++++++++++++++----- src/gwproto/data_classes/sh_node.py | 19 +++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 17c0022f..d07ff0cd 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -159,10 +159,25 @@ def load_components( errors.append(LoadError(type_name, component_dict, e)) return components + @classmethod + def make_node(cls, node_dict: dict, components: dict[str, Component]) -> ShNode: + component_id = node_dict.get("ComponentId") + if component_id: + component = components.get(component_id) + if component is None: + raise ValueError( + f"ERROR. Component <{component_id}> not loaded " + f"for node <{node_dict.get('Alias')}>" + ) + else: + component = None + return ShNode(component=component, **node_dict) + @classmethod def load_nodes( cls, layout: dict[Any, Any], + components: dict[str, Component], *, raise_errors: bool = True, errors: Optional[list[LoadError]] = None, @@ -175,7 +190,7 @@ def load_nodes( try: node_name = node_dict["Alias"] if included_node_names is None or node_name in included_node_names: - nodes[node_name] = ShNode.model_validate(node_dict) + nodes[node_name] = cls.make_node(node_dict, components) except Exception as e: # noqa: PERF203 if raise_errors: raise @@ -281,17 +296,19 @@ def load_dict( # noqa: PLR0913 errors=errors, cac_decoder=cac_decoder, ) + components = cls.load_components( + layout=layout, + cacs=cacs, + raise_errors=raise_errors, + errors=errors, + component_decoder=component_decoder, + ) load_args = { "cacs": cacs, - "components": cls.load_components( - layout=layout, - cacs=cacs, - raise_errors=raise_errors, - errors=errors, - component_decoder=component_decoder, - ), + "components": components, "nodes": cls.load_nodes( layout=layout, + components=components, raise_errors=raise_errors, errors=errors, included_node_names=included_node_names, diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 89362b46..57e37125 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -2,8 +2,9 @@ from typing import Optional +from pydantic import ConfigDict + from gwproto.data_classes.components.component import Component -from gwproto.data_classes.errors import DataClassLoadingError from gwproto.enums import ActorClass, Role from gwproto.types import SpaceheatNodeGt @@ -17,6 +18,12 @@ class ShNode(SpaceheatNodeGt): temperature data for the purposes of thermostatic control). """ + component: Optional[Component] = None + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __hash__(self) -> int: + return hash((type(self), *self.__dict__.values())) + @property def sh_node_id(self) -> str: return self.ShNodeId @@ -60,13 +67,3 @@ def __repr__(self) -> str: @property def has_actor(self) -> bool: return self.actor_class != ActorClass.NoActor - - @property - def component(self) -> Optional[Component]: - if self.component_id is None: - return None - if self.component_id not in Component.by_id: - raise DataClassLoadingError( - f"{self.alias} component {self.component_id} not loaded!" - ) - return Component.by_id[self.component_id] From 0e46abeaabc505dfd47bb1c72c21fa2e3ab827f7 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 12 Sep 2024 14:09:01 -0400 Subject: [PATCH 106/168] ShNode hash relies only on uuid, which should be stable without loss of uniquenenss --- src/gwproto/data_classes/sh_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 57e37125..1b9bdf39 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -22,7 +22,7 @@ class ShNode(SpaceheatNodeGt): model_config = ConfigDict(arbitrary_types_allowed=True) def __hash__(self) -> int: - return hash((type(self), *self.__dict__.values())) + return hash(self.ShNodeId) @property def sh_node_id(self) -> str: From 6452c50ccf706dc31cef0ad0de2d1c06538b0cfe Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 13 Sep 2024 12:48:21 -0400 Subject: [PATCH 107/168] Removed message _Maker classes - Added UTCSeconds and UTCMilliseconds to validate timestamps without explicit in-class validators. - int representing bools converted to bools. - Message payload tests with makers reduced to serializing and deserializing the test dicts. - Gs classes still have _Makers. --- pyproject.toml | 2 + src/gwproto/messages/__init__.py | 13 - src/gwproto/property_format.py | 36 +- src/gwproto/types/__init__.py | 41 +- src/gwproto/types/data_channel.py | 228 +--------- src/gwproto/types/gt_dispatch_boolean.py | 351 +--------------- .../types/gt_dispatch_boolean_local.py | 278 +------------ .../types/gt_driver_booleanactuator_cmd.py | 251 +---------- .../types/gt_sh_booleanactuator_cmd_status.py | 223 +--------- src/gwproto/types/gt_sh_cli_atn_cmd.py | 239 +---------- .../gt_sh_multipurpose_telemetry_status.py | 260 +----------- .../types/gt_sh_simple_telemetry_status.py | 250 +---------- src/gwproto/types/gt_sh_status.py | 393 +----------------- ...t_sh_telemetry_from_multipurpose_sensor.py | 262 +----------- src/gwproto/types/gt_telemetry.py | 217 +--------- src/gwproto/types/heartbeat_b.py | 349 +--------------- src/gwproto/types/power_watts.py | 140 +------ src/gwproto/types/snapshot_spaceheat.py | 253 +---------- src/gwproto/types/ta_data_channels.py | 338 +-------------- .../types/telemetry_snapshot_spaceheat.py | 281 +------------ tests/data/snapshot_message.json | 4 +- tests/data/status_message.json | 26 +- tests/data_classes/test_electric_meter_cac.py | 42 +- tests/dummy_decoders/child/codec.py | 5 - tests/dummy_decoders/parent/codec.py | 5 - tests/test_decoders.py | 56 ++- tests/types/test_data_channel.py | 92 +--- tests/types/test_gt_dispatch_boolean.py | 123 +----- tests/types/test_gt_dispatch_boolean_local.py | 103 +---- .../test_gt_driver_booleanactuator_cmd.py | 93 +---- .../test_gt_sh_booleanactuator_cmd_status.py | 80 +--- tests/types/test_gt_sh_cli_atn_cmd.py | 84 +--- ...est_gt_sh_multipurpose_telemetry_status.py | 98 +---- .../test_gt_sh_simple_telemetry_status.py | 92 +--- tests/types/test_gt_sh_status.py | 176 +------- ...t_sh_telemetry_from_multipurpose_sensor.py | 92 +--- tests/types/test_gt_telemetry.py | 100 +---- tests/types/test_heartbeat_b.py | 132 +----- tests/types/test_power_watts.py | 58 +-- tests/types/test_snapshot_spaceheat.py | 82 +--- tests/types/test_ta_data_channels.py | 124 +----- .../test_telemetry_snapshot_spaceheat.py | 92 +--- 42 files changed, 281 insertions(+), 5883 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c1a9c75f..9104f16f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,6 +130,8 @@ ignore = [ "E272", # Formatter "E241", # Formatter "E302", + "E303", # Formatter + "E305", # Formatter "E501", "EM", "FA", # We only support Python >= 3.10, so we shouldn't need this diff --git a/src/gwproto/messages/__init__.py b/src/gwproto/messages/__init__.py index 75094d87..75be84a7 100644 --- a/src/gwproto/messages/__init__.py +++ b/src/gwproto/messages/__init__.py @@ -19,25 +19,15 @@ "GsPwr_Maker", "GtDispatchBoolean", "GtDispatchBooleanLocal", - "GtDispatchBooleanLocal_Maker", - "GtDispatchBoolean_Maker", "GtDriverBooleanactuatorCmd", - "GtDriverBooleanactuatorCmd_Maker", "GtShBooleanactuatorCmdStatus", - "GtShBooleanactuatorCmdStatus_Maker", "GtShCliAtnCmd", - "GtShCliAtnCmd_Maker", "GtShMultipurposeTelemetryStatus", - "GtShMultipurposeTelemetryStatus_Maker", "GtShSimpleTelemetryStatus", - "GtShSimpleTelemetryStatus_Maker", "GtShStatus", "GtShStatusEvent", - "GtShStatus_Maker", "GtShTelemetryFromMultipurposeSensor", - "GtShTelemetryFromMultipurposeSensor_Maker", "GtTelemetry", - "GtTelemetry_Maker", "MQTTConnectEvent", "MQTTConnectFailedEvent", "MQTTDisconnectEvent", @@ -46,15 +36,12 @@ "Ping", "PingMessage", "PowerWatts", - "PowerWatts_Maker", "ProblemEvent", "Problems", "ResponseTimeoutEvent", "ShutdownEvent", "SnapshotSpaceheat", "SnapshotSpaceheatEvent", - "SnapshotSpaceheat_Maker", "StartupEvent", "TelemetrySnapshotSpaceheat", - "TelemetrySnapshotSpaceheat_Maker", ] diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index f482959d..94662ffc 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -7,7 +7,7 @@ from typing import Annotated, Any, Callable, List import pydantic -from pydantic import BeforeValidator +from pydantic import BeforeValidator, Field def predicate_validator( @@ -75,6 +75,29 @@ def is_hex_char(v: str) -> bool: return not v not in "0123456789abcdefABCDEF" +def check_is_hex_char(v: str) -> None: + """Checks HexChar format + + HexChar format: single-char string in '0123456789abcdefABCDEF' + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not HexChar format + """ + if not isinstance(v, str): + raise ValueError(f"<{v}> must be string. Got type <{type(v)}") # noqa: TRY004 + if len(v) > 1: + raise ValueError(f"<{v}> must be a hex char, but not of len 1") + if v not in "0123456789abcdefABCDEF": + raise ValueError(f"<{v}> must be one of '0123456789abcdefABCDEF'") + return v + + +HexChar = Annotated[str, BeforeValidator(check_is_hex_char)] + + def is_valid_asa_name(candidate: str) -> bool: """a string no more than 32 chars @@ -260,6 +283,17 @@ def is_reasonable_unix_time_ms(candidate: int) -> bool: ) +UTC_2000_01_01_TIMESTAMP = datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() +UTC_3000_01_01_TIMESTAMP = datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() + +UTCSeconds = Annotated[ + int, Field(ge=UTC_2000_01_01_TIMESTAMP, le=UTC_3000_01_01_TIMESTAMP) +] +UTCMilliseconds = Annotated[ + int, Field(ge=UTC_2000_01_01_TIMESTAMP * 1000, le=UTC_3000_01_01_TIMESTAMP * 1000) +] + + def check_is_reasonable_unix_time_ms(candidate: int) -> None: if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > candidate: raise ValueError("ReasonableUnixTimeMs must be after 2000 AD") diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index e2978e29..4262665e 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -2,7 +2,7 @@ from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt -from gwproto.types.data_channel import DataChannel, DataChannel_Maker +from gwproto.types.data_channel import DataChannel from gwproto.types.egauge_io import EgaugeIo from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, @@ -16,35 +16,29 @@ from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, ) -from gwproto.types.gt_dispatch_boolean import GtDispatchBoolean, GtDispatchBoolean_Maker +from gwproto.types.gt_dispatch_boolean import GtDispatchBoolean from gwproto.types.gt_dispatch_boolean_local import ( GtDispatchBooleanLocal, - GtDispatchBooleanLocal_Maker, ) from gwproto.types.gt_driver_booleanactuator_cmd import ( GtDriverBooleanactuatorCmd, - GtDriverBooleanactuatorCmd_Maker, ) from gwproto.types.gt_sh_booleanactuator_cmd_status import ( GtShBooleanactuatorCmdStatus, - GtShBooleanactuatorCmdStatus_Maker, ) -from gwproto.types.gt_sh_cli_atn_cmd import GtShCliAtnCmd, GtShCliAtnCmd_Maker +from gwproto.types.gt_sh_cli_atn_cmd import GtShCliAtnCmd from gwproto.types.gt_sh_multipurpose_telemetry_status import ( GtShMultipurposeTelemetryStatus, - GtShMultipurposeTelemetryStatus_Maker, ) from gwproto.types.gt_sh_simple_telemetry_status import ( GtShSimpleTelemetryStatus, - GtShSimpleTelemetryStatus_Maker, ) -from gwproto.types.gt_sh_status import GtShStatus, GtShStatus_Maker +from gwproto.types.gt_sh_status import GtShStatus from gwproto.types.gt_sh_telemetry_from_multipurpose_sensor import ( GtShTelemetryFromMultipurposeSensor, - GtShTelemetryFromMultipurposeSensor_Maker, ) -from gwproto.types.gt_telemetry import GtTelemetry, GtTelemetry_Maker -from gwproto.types.heartbeat_b import HeartbeatB, HeartbeatB_Maker +from gwproto.types.gt_telemetry import GtTelemetry +from gwproto.types.heartbeat_b import HeartbeatB from gwproto.types.hubitat_cac_gt import HubitatCacGt from gwproto.types.hubitat_component_gt import ( HubitatComponentGt, @@ -68,7 +62,7 @@ from gwproto.types.pipe_flow_sensor_component_gt import ( PipeFlowSensorComponentGt, ) -from gwproto.types.power_watts import PowerWatts, PowerWatts_Maker +from gwproto.types.power_watts import PowerWatts from gwproto.types.relay_cac_gt import RelayCacGt from gwproto.types.relay_component_gt import RelayComponentGt from gwproto.types.resistive_heater_cac_gt import ( @@ -87,15 +81,14 @@ from gwproto.types.simple_temp_sensor_component_gt import ( SimpleTempSensorComponentGt, ) -from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat, SnapshotSpaceheat_Maker +from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt -from gwproto.types.ta_data_channels import TaDataChannels, TaDataChannels_Maker +from gwproto.types.ta_data_channels import TaDataChannels from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, ) from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, - TelemetrySnapshotSpaceheat_Maker, ) from gwproto.types.web_server_gt import WebServerGt @@ -103,7 +96,6 @@ "ComponentAttributeClassGt", "ComponentGt", "DataChannel", - "DataChannel_Maker", "EgaugeIo", "EgaugeRegisterConfig", "ElectricMeterCacGt", @@ -112,26 +104,15 @@ "FibaroSmartImplantComponentGt", "GtDispatchBoolean", "GtDispatchBooleanLocal", - "GtDispatchBooleanLocal_Maker", - "GtDispatchBoolean_Maker", "GtDriverBooleanactuatorCmd", - "GtDriverBooleanactuatorCmd_Maker", "GtShBooleanactuatorCmdStatus", - "GtShBooleanactuatorCmdStatus_Maker", "GtShCliAtnCmd", - "GtShCliAtnCmd_Maker", "GtShMultipurposeTelemetryStatus", - "GtShMultipurposeTelemetryStatus_Maker", "GtShSimpleTelemetryStatus", - "GtShSimpleTelemetryStatus_Maker", "GtShStatus", - "GtShStatus_Maker", "GtShTelemetryFromMultipurposeSensor", - "GtShTelemetryFromMultipurposeSensor_Maker", "GtTelemetry", - "GtTelemetry_Maker", "HeartbeatB", - "HeartbeatB_Maker", "HubitatCacGt", "HubitatComponentGt", "HubitatPollerCacGt", @@ -143,7 +124,6 @@ "PipeFlowSensorCacGt", "PipeFlowSensorComponentGt", "PowerWatts", - "PowerWatts_Maker", "RESTPollerCacGt", "RESTPollerComponentGt", "RelayCacGt", @@ -153,13 +133,10 @@ "SimpleTempSensorCacGt", "SimpleTempSensorComponentGt", "SnapshotSpaceheat", - "SnapshotSpaceheat_Maker", "SpaceheatNodeGt", "TaDataChannels", - "TaDataChannels_Maker", "TelemetryReportingConfig", "TelemetrySnapshotSpaceheat", - "TelemetrySnapshotSpaceheat_Maker", "WebServerGt", "cacs", # noqa: F822 "components", # noqa: F822 diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py index ef03e804..6acb8d0e 100644 --- a/src/gwproto/types/data_channel.py +++ b/src/gwproto/types/data_channel.py @@ -1,19 +1,11 @@ """Type data.channel, version 000""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.enums import TelemetryName as EnumTelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.enums import TelemetryName +from gwproto.property_format import LeftRightDotStr class DataChannel(BaseModel): @@ -32,214 +24,8 @@ class DataChannel(BaseModel): "unique within the data channels associated to a specific Terminal Asset." ), ) - AboutName: str = Field( - title="About Name", - description="The name of the SpaceheatNode whose physical quantities are getting captured.", - ) - CapturedByName: str = Field( - title="CapturedByName", - description=( - "The name of the SpaceheatNode that is capturing the physical quantities (which can " - "be AboutName but does not have to be)." - ), - ) - TelemetryName: EnumTelemetryName = Field( - title="TelemetryName", - description="The name of the physical quantity getting measured.", - ) + AboutName: LeftRightDotStr + CapturedByName: LeftRightDotStr + TelemetryName: TelemetryName TypeName: Literal["data.channel"] = "data.channel" Version: Literal["000"] = "000" - - @field_validator("AboutName") - @classmethod - def _check_about_name(cls, v: str) -> str: - try: - check_is_spaceheat_name(v) - except ValueError as e: - raise ValueError(f"AboutName failed SpaceheatName format validation: {e}") - return v - - @field_validator("CapturedByName") - @classmethod - def _check_captured_by_name(cls, v: str) -> str: - try: - check_is_spaceheat_name(v) - except ValueError as e: - raise ValueError( - f"CapturedByName failed SpaceheatName format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - data.channel.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - data.channel.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["TelemetryName"] - d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( - self.TelemetryName - ) - return d - - def as_type(self) -> bytes: - """ - Serialize to the data.channel.000 representation. - - Instances in the class are python-native representations of data.channel.000 - objects, while the actual data.channel.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is DataChannel.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class DataChannel_Maker: - type_name = "data.channel" - version = "000" - - def __init__( - self, - display_name: str, - about_name: str, - captured_by_name: str, - telemetry_name: EnumTelemetryName, - ) -> None: - self.tuple = DataChannel( - DisplayName=display_name, - AboutName=about_name, - CapturedByName=captured_by_name, - TelemetryName=telemetry_name, - ) - - @classmethod - def tuple_to_type(cls, tpl: DataChannel) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> DataChannel: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> DataChannel: - """ - Deserialize a dictionary representation of a data.channel.000 message object - into a DataChannel python object for internal use. - - This is the near-inverse of the DataChannel.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a DataChannel object. - - Returns: - DataChannel - """ - d2 = dict(d) - if "DisplayName" not in d2: - raise SchemaError(f"dict missing DisplayName: <{d2}>") - if "AboutName" not in d2: - raise SchemaError(f"dict missing AboutName: <{d2}>") - if "CapturedByName" not in d2: - raise SchemaError(f"dict missing CapturedByName: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2: - raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") - value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) - d2["TelemetryName"] = EnumTelemetryName(value) - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret data.channel version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return DataChannel(**d2) - - -def check_is_spaceheat_name(v: str) -> None: - """Check SpaceheatName Format. - - Validates if the provided string adheres to the SpaceheatName format: - Lowercase words separated by periods, where word characters can be alphanumeric - or a hyphen, and the first word starts with an alphabet character. - - Args: - v (str): The string to be validated. - - Raises: - ValueError: If the provided string is not in SpaceheatName format. - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - for char in word: - if not (char.isalnum() or char == "-"): - raise ValueError( - f"words of <{v}> split by by '.' must be alphanumeric or hyphen." - ) - if not v.islower(): - raise ValueError(f"<{v}> must be lowercase.") diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index c4d9dbe0..3d91f388 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -1,19 +1,10 @@ """Type gt.dispatch.boolean, version 110""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds, UUID4Str class GtDispatchBoolean(BaseModel): @@ -23,336 +14,20 @@ class GtDispatchBoolean(BaseModel): Boolean dispatch command designed to be sent from an AtomicTNode to a SCADA. """ - AboutNodeName: str = Field( - title="The Spaceheat Node getting dispatched", - ) - ToGNodeAlias: str = Field( - title="GNodeAlias of the SCADA", - description="[More info](https://gridworks.readthedocs.io/en/latest/scada.html)", - ) - FromGNodeAlias: str = Field( - title="GNodeAlias of AtomicTNode", - description="[More info](https://gridworks.readthedocs.io/en/latest/atomic-t-node.html)", - ) - FromGNodeInstanceId: str = Field( - title="GNodeInstance of the AtomicTNode", - ) - RelayState: int = Field( - title="Relay State (0 or 1)", + AboutNodeName: LeftRightDotStr + ToGNodeAlias: LeftRightDotStr + FromGNodeAlias: LeftRightDotStr + FromGNodeInstanceId: UUID4Str + RelayState: bool = Field( + title="Relay State (False or True)", description=( - "A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates " - "the relay is CLOSED (on). Note that `0` means the relay is open whether or not the " + "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " + "the relay is CLOSED (on). Note that `False` means the relay is open whether or not the " "relay is normally open or normally closed (For a normally open relay, the relay " - "is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.)" + "is ENERGIZED when it is in state `False` and DE-ENERGIZED when it is in state `True`.)" "[More info](https://gridworks.readthedocs.io/en/latest/relay-state.html)" ), ) - SendTimeUnixMs: int = Field( - title="Time the AtomicTNode sends the dispatch, by its clock", - ) + SendTimeUnixMs: UTCMilliseconds TypeName: Literal["gt.dispatch.boolean"] = "gt.dispatch.boolean" Version: Literal["110"] = "110" - - @field_validator("AboutNodeName") - @classmethod - def _check_about_node_name(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"AboutNodeName failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("ToGNodeAlias") - @classmethod - def _check_to_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"ToGNodeAlias failed LeftRightDot format validation: {e}") - return v - - @field_validator("FromGNodeAlias") - @classmethod - def _check_from_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"FromGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromGNodeInstanceId") - @classmethod - def _check_from_g_node_instance_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"FromGNodeInstanceId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("RelayState", mode="before") - @classmethod - def _check_relay_state(cls, v: int) -> int: - try: - check_is_bit(v) - except ValueError as e: - raise ValueError(f"RelayState failed Bit format validation: {e}") - return v - - @field_validator("SendTimeUnixMs") - @classmethod - def _check_send_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"SendTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.dispatch.boolean.110 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.dispatch.boolean.110 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the gt.dispatch.boolean.110 representation. - - Instances in the class are python-native representations of gt.dispatch.boolean.110 - objects, while the actual gt.dispatch.boolean.110 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtDispatchBoolean.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtDispatchBoolean_Maker: - type_name = "gt.dispatch.boolean" - version = "110" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - about_node_name: str, - to_g_node_alias: str, - from_g_node_alias: str, - from_g_node_instance_id: str, - relay_state: int, - send_time_unix_ms: int, - ) -> None: - self.tuple = GtDispatchBoolean( - AboutNodeName=about_node_name, - ToGNodeAlias=to_g_node_alias, - FromGNodeAlias=from_g_node_alias, - FromGNodeInstanceId=from_g_node_instance_id, - RelayState=relay_state, - SendTimeUnixMs=send_time_unix_ms, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtDispatchBoolean) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtDispatchBoolean: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtDispatchBoolean: - """ - Deserialize a dictionary representation of a gt.dispatch.boolean.110 message object - into a GtDispatchBoolean python object for internal use. - - This is the near-inverse of the GtDispatchBoolean.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtDispatchBoolean object. - - Returns: - GtDispatchBoolean - """ - d2 = dict(d) - if "AboutNodeName" not in d2: - raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "ToGNodeAlias" not in d2: - raise SchemaError(f"dict missing ToGNodeAlias: <{d2}>") - if "FromGNodeAlias" not in d2: - raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2: - raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "RelayState" not in d2: - raise SchemaError(f"dict missing RelayState: <{d2}>") - if "SendTimeUnixMs" not in d2: - raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "110": - LOGGER.debug( - f"Attempting to interpret gt.dispatch.boolean version {d2['Version']} as version 110" - ) - d2["Version"] = "110" - return GtDispatchBoolean(**d2) - - -def check_is_bit(v: int) -> None: - """ - Checks Bit format - - Bit format: The value must be the integer 0 or the integer 1. - - Will not attempt to first interpret as an integer. For example, - 1.3 will not be interpreted as 1 but will raise an error. - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not 0 or 1 - """ - if v not in {0, 1}: - raise ValueError(f"<{v}> must be 0 or 1") - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 14152c91..129b6401 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -1,19 +1,10 @@ """Type gt.dispatch.boolean.local, version 110""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds class GtDispatchBooleanLocal(BaseModel): @@ -24,265 +15,18 @@ class GtDispatchBooleanLocal(BaseModel): within the Local Area Network of the SCADA - typically it should reside on the same hardware. """ - RelayState: int = Field( - title="Relay State (0 or 1)", + AboutNodeName: LeftRightDotStr + FromNodeName: LeftRightDotStr + RelayState: bool = Field( + title="Relay State (False or True)", description=( - "A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates " - "the relay is CLOSED (on). Note that `0` means the relay is open whether or not the " + "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " + "the relay is CLOSED (on). Note that `False` means the relay is open whether or not the " "relay is normally open or normally closed (For a normally open relay, the relay " - "is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.)" + "is ENERGIZED when it is in state `False` and DE-ENERGIZED when it is in state `True`.)" "[More info](https://gridworks.readthedocs.io/en/latest/relay-state.html)" ), ) - AboutNodeName: str = Field( - title="About Node Name", - description="The boolean actuator Spaceheat Node getting turned on or off.", - ) - FromNodeName: str = Field( - title="From Node Name", - description="The Spaceheat Node sending the command.", - ) - SendTimeUnixMs: int = Field( - title="Send Time in Unix Milliseconds", - ) + SendTimeUnixMs: UTCMilliseconds TypeName: Literal["gt.dispatch.boolean.local"] = "gt.dispatch.boolean.local" Version: Literal["110"] = "110" - - @field_validator("RelayState", mode="before") - @classmethod - def _check_relay_state(cls, v: int) -> int: - try: - check_is_bit(v) - except ValueError as e: - raise ValueError(f"RelayState failed Bit format validation: {e}") - return v - - @field_validator("AboutNodeName") - @classmethod - def _check_about_node_name(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"AboutNodeName failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromNodeName") - @classmethod - def _check_from_node_name(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"FromNodeName failed LeftRightDot format validation: {e}") - return v - - @field_validator("SendTimeUnixMs") - @classmethod - def _check_send_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"SendTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.dispatch.boolean.local.110 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.dispatch.boolean.local.110 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the gt.dispatch.boolean.local.110 representation. - - Instances in the class are python-native representations of gt.dispatch.boolean.local.110 - objects, while the actual gt.dispatch.boolean.local.110 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtDispatchBooleanLocal.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtDispatchBooleanLocal_Maker: - type_name = "gt.dispatch.boolean.local" - version = "110" - - def __init__( - self, - relay_state: int, - about_node_name: str, - from_node_name: str, - send_time_unix_ms: int, - ) -> None: - self.tuple = GtDispatchBooleanLocal( - RelayState=relay_state, - AboutNodeName=about_node_name, - FromNodeName=from_node_name, - SendTimeUnixMs=send_time_unix_ms, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtDispatchBooleanLocal) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtDispatchBooleanLocal: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtDispatchBooleanLocal: - """ - Deserialize a dictionary representation of a gt.dispatch.boolean.local.110 message object - into a GtDispatchBooleanLocal python object for internal use. - - This is the near-inverse of the GtDispatchBooleanLocal.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtDispatchBooleanLocal object. - - Returns: - GtDispatchBooleanLocal - """ - d2 = dict(d) - if "RelayState" not in d2: - raise SchemaError(f"dict missing RelayState: <{d2}>") - if "AboutNodeName" not in d2: - raise SchemaError(f"dict missing AboutNodeName: <{d2}>") - if "FromNodeName" not in d2: - raise SchemaError(f"dict missing FromNodeName: <{d2}>") - if "SendTimeUnixMs" not in d2: - raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "110": - LOGGER.debug( - f"Attempting to interpret gt.dispatch.boolean.local version {d2['Version']} as version 110" - ) - d2["Version"] = "110" - return GtDispatchBooleanLocal(**d2) - - -def check_is_bit(v: int) -> None: - """ - Checks Bit format - - Bit format: The value must be the integer 0 or the integer 1. - - Will not attempt to first interpret as an integer. For example, - 1.3 will not be interpreted as 1 but will raise an error. - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not 0 or 1 - """ - if v not in {0, 1}: - raise ValueError(f"<{v}> must be 0 or 1") - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index 1994c7be..c37a8db0 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -1,19 +1,10 @@ """Type gt.driver.booleanactuator.cmd, version 100""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds class GtDriverBooleanactuatorCmd(BaseModel): @@ -26,238 +17,8 @@ class GtDriverBooleanactuatorCmd(BaseModel): [More info](https://gridworks.readthedocs.io/en/latest/relay-state.html) """ - RelayState: int = Field( - title="RelayState", - ) - ShNodeAlias: str = Field( - title="ShNodeAlias", - ) - CommandTimeUnixMs: int = Field( - title="CommandTimeUnixMs", - ) + RelayState: bool + ShNodeAlias: LeftRightDotStr + CommandTimeUnixMs: UTCMilliseconds TypeName: Literal["gt.driver.booleanactuator.cmd"] = "gt.driver.booleanactuator.cmd" Version: Literal["100"] = "100" - - @field_validator("RelayState", mode="before") - @classmethod - def _check_relay_state(cls, v: int) -> int: - try: - check_is_bit(v) - except ValueError as e: - raise ValueError(f"RelayState failed Bit format validation: {e}") - return v - - @field_validator("ShNodeAlias") - @classmethod - def _check_sh_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") - return v - - @field_validator("CommandTimeUnixMs") - @classmethod - def _check_command_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"CommandTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.driver.booleanactuator.cmd.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.driver.booleanactuator.cmd.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the gt.driver.booleanactuator.cmd.100 representation. - - Instances in the class are python-native representations of gt.driver.booleanactuator.cmd.100 - objects, while the actual gt.driver.booleanactuator.cmd.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtDriverBooleanactuatorCmd.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtDriverBooleanactuatorCmd_Maker: - type_name = "gt.driver.booleanactuator.cmd" - version = "100" - - def __init__( - self, - relay_state: int, - sh_node_alias: str, - command_time_unix_ms: int, - ) -> None: - self.tuple = GtDriverBooleanactuatorCmd( - RelayState=relay_state, - ShNodeAlias=sh_node_alias, - CommandTimeUnixMs=command_time_unix_ms, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtDriverBooleanactuatorCmd) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtDriverBooleanactuatorCmd: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtDriverBooleanactuatorCmd: - """ - Deserialize a dictionary representation of a gt.driver.booleanactuator.cmd.100 message object - into a GtDriverBooleanactuatorCmd python object for internal use. - - This is the near-inverse of the GtDriverBooleanactuatorCmd.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtDriverBooleanactuatorCmd object. - - Returns: - GtDriverBooleanactuatorCmd - """ - d2 = dict(d) - if "RelayState" not in d2: - raise SchemaError(f"dict missing RelayState: <{d2}>") - if "ShNodeAlias" not in d2: - raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "CommandTimeUnixMs" not in d2: - raise SchemaError(f"dict missing CommandTimeUnixMs: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret gt.driver.booleanactuator.cmd version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return GtDriverBooleanactuatorCmd(**d2) - - -def check_is_bit(v: int) -> None: - """ - Checks Bit format - - Bit format: The value must be the integer 0 or the integer 1. - - Will not attempt to first interpret as an integer. For example, - 1.3 will not be interpreted as 1 but will raise an error. - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not 0 or 1 - """ - if v not in {0, 1}: - raise ValueError(f"<{v}> must be 0 or 1") - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py index 854ee212..1f39336a 100644 --- a/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py +++ b/src/gwproto/types/gt_sh_booleanactuator_cmd_status.py @@ -1,19 +1,10 @@ """Type gt.sh.booleanactuator.cmd.status, version 100""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr class GtShBooleanactuatorCmdStatus(BaseModel): @@ -27,218 +18,16 @@ class GtShBooleanactuatorCmdStatus(BaseModel): [More info](https://gridworks.readthedocs.io/en/latest/relay-state.html) """ - ShNodeAlias: str = Field( - title="SpaceheatNodeAlias", + ShNodeAlias: LeftRightDotStr = Field( description=( "The alias of the spaceheat node that is getting actuated. For example, `a.elt1.relay` " "would likely indicate the relay for a resistive element." "[More info](https://gridworks-protocol.readthedocs.io/en/latest/boolean-actuator.html)" ), ) - RelayStateCommandList: List[int] = Field( - title="List of RelayStateCommands", - ) - CommandTimeUnixMsList: List[int] = Field( - title="List of Command Times", - ) + RelayStateCommandList: list[bool] + CommandTimeUnixMsList: list[int] TypeName: Literal["gt.sh.booleanactuator.cmd.status"] = ( "gt.sh.booleanactuator.cmd.status" ) Version: Literal["100"] = "100" - - @field_validator("ShNodeAlias") - @classmethod - def _check_sh_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") - return v - - @field_validator("CommandTimeUnixMsList") - @classmethod - def _check_command_time_unix_ms_list(cls, v: List[int]) -> List[int]: - for elt in v: - try: - check_is_reasonable_unix_time_ms(elt) - except ValueError as e: # noqa: PERF203 - raise ValueError( - f"CommandTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.booleanactuator.cmd.status.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.booleanactuator.cmd.status.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.booleanactuator.cmd.status.100 representation. - - Instances in the class are python-native representations of gt.sh.booleanactuator.cmd.status.100 - objects, while the actual gt.sh.booleanactuator.cmd.status.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShBooleanactuatorCmdStatus.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShBooleanactuatorCmdStatus_Maker: - type_name = "gt.sh.booleanactuator.cmd.status" - version = "100" - - def __init__( - self, - sh_node_alias: str, - relay_state_command_list: List[int], - command_time_unix_ms_list: List[int], - ) -> None: - self.tuple = GtShBooleanactuatorCmdStatus( - ShNodeAlias=sh_node_alias, - RelayStateCommandList=relay_state_command_list, - CommandTimeUnixMsList=command_time_unix_ms_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShBooleanactuatorCmdStatus) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShBooleanactuatorCmdStatus: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShBooleanactuatorCmdStatus: - """ - Deserialize a dictionary representation of a gt.sh.booleanactuator.cmd.status.100 message object - into a GtShBooleanactuatorCmdStatus python object for internal use. - - This is the near-inverse of the GtShBooleanactuatorCmdStatus.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShBooleanactuatorCmdStatus object. - - Returns: - GtShBooleanactuatorCmdStatus - """ - d2 = dict(d) - if "ShNodeAlias" not in d2: - raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "RelayStateCommandList" not in d2: - raise SchemaError(f"dict missing RelayStateCommandList: <{d2}>") - if "CommandTimeUnixMsList" not in d2: - raise SchemaError(f"dict missing CommandTimeUnixMsList: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret gt.sh.booleanactuator.cmd.status version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return GtShBooleanactuatorCmdStatus(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_cli_atn_cmd.py b/src/gwproto/types/gt_sh_cli_atn_cmd.py index b80d100b..774fd6ca 100644 --- a/src/gwproto/types/gt_sh_cli_atn_cmd.py +++ b/src/gwproto/types/gt_sh_cli_atn_cmd.py @@ -1,18 +1,10 @@ """Type gt.sh.cli.atn.cmd, version 110""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UUID4Str class GtShCliAtnCmd(BaseModel): @@ -23,10 +15,7 @@ class GtShCliAtnCmd(BaseModel): by the AtomicTNode. """ - FromGNodeAlias: str = Field( - title="GNodeAlias", - description="Must be the SCADA's AtomicTNode.", - ) + FromGNodeAlias: LeftRightDotStr SendSnapshot: bool = Field( title="Send Snapshot", description=( @@ -35,224 +24,6 @@ class GtShCliAtnCmd(BaseModel): "variations are added later." ), ) - FromGNodeId: str = Field( - title="GNodeId", - ) + FromGNodeId: UUID4Str TypeName: Literal["gt.sh.cli.atn.cmd"] = "gt.sh.cli.atn.cmd" Version: Literal["110"] = "110" - - @field_validator("FromGNodeAlias") - @classmethod - def _check_from_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"FromGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromGNodeId") - @classmethod - def _check_from_g_node_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"FromGNodeId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.cli.atn.cmd.110 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.cli.atn.cmd.110 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.cli.atn.cmd.110 representation. - - Instances in the class are python-native representations of gt.sh.cli.atn.cmd.110 - objects, while the actual gt.sh.cli.atn.cmd.110 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShCliAtnCmd.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShCliAtnCmd_Maker: - type_name = "gt.sh.cli.atn.cmd" - version = "110" - - def __init__( - self, - from_g_node_alias: str, - send_snapshot: bool, # noqa: FBT001 - from_g_node_id: str, - ) -> None: - self.tuple = GtShCliAtnCmd( - FromGNodeAlias=from_g_node_alias, - SendSnapshot=send_snapshot, - FromGNodeId=from_g_node_id, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShCliAtnCmd) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShCliAtnCmd: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShCliAtnCmd: - """ - Deserialize a dictionary representation of a gt.sh.cli.atn.cmd.110 message object - into a GtShCliAtnCmd python object for internal use. - - This is the near-inverse of the GtShCliAtnCmd.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShCliAtnCmd object. - - Returns: - GtShCliAtnCmd - """ - d2 = dict(d) - if "FromGNodeAlias" not in d2: - raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "SendSnapshot" not in d2: - raise SchemaError(f"dict missing SendSnapshot: <{d2}>") - if "FromGNodeId" not in d2: - raise SchemaError(f"dict missing FromGNodeId: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "110": - LOGGER.debug( - f"Attempting to interpret gt.sh.cli.atn.cmd version {d2['Version']} as version 110" - ) - d2["Version"] = "110" - return GtShCliAtnCmd(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index be831ba1..154591bc 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -1,20 +1,14 @@ """Type gt.sh.multipurpose.telemetry.status, version 100""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal, Self +from typing import Literal, Self -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel, Field, model_validator -from gwproto.enums import TelemetryName as EnumTelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" +from gwproto.enums.telemetry_name import TelemetryName +from gwproto.property_format import ( + LeftRightDotStr, + UTCMilliseconds, ) -LOGGER = logging.getLogger(__name__) class GtShMultipurposeTelemetryStatus(BaseModel): @@ -29,8 +23,7 @@ class GtShMultipurposeTelemetryStatus(BaseModel): [More info](https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html) """ - AboutNodeAlias: str = Field( - title="AboutNodeAlias", + AboutNodeAlias: LeftRightDotStr = Field( description=( "The SpaceheatNode representing the physical object that the sensor reading is collecting " "data about. For example, a multipurpose temp sensor that reads 12 temperatures would " @@ -39,55 +32,15 @@ class GtShMultipurposeTelemetryStatus(BaseModel): "[More info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html)" ), ) - SensorNodeAlias: str = Field( - title="SensorNodeAlias", - description="The alias of the SpaceheatNode representing the telemetry device", - ) - TelemetryName: EnumTelemetryName = Field( - title="TelemetryName", - description=( - "The TelemetryName of the readings. This is used to interpet the meaning of the reading " - "values. For example, WaterTempCTimes1000 means the reading is measuring the a reading " - "of 37 deg C." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/telemetry-name.html)" - ), - ) - ValueList: List[int] = Field( - title="List of Values", - description="The values of the readings.", - ) - ReadTimeUnixMsList: List[int] = Field( - title="List of Read Times", - description="The times that the MultipurposeSensor took the readings, in unix milliseconds", - ) + SensorNodeAlias: LeftRightDotStr + TelemetryName: TelemetryName + ValueList: list[int] + ReadTimeUnixMsList: list[UTCMilliseconds] TypeName: Literal["gt.sh.multipurpose.telemetry.status"] = ( "gt.sh.multipurpose.telemetry.status" ) Version: Literal["100"] = "100" - @field_validator("AboutNodeAlias") - @classmethod - def _check_about_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"AboutNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("ReadTimeUnixMsList") - @classmethod - def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: - for elt in v: - try: - check_is_reasonable_unix_time_ms(elt) - except ValueError as e: # noqa: PERF203 - raise ValueError( - f"ReadTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - @model_validator(mode="after") def check_axiom_1(self) -> Self: """ @@ -99,194 +52,3 @@ def check_axiom_1(self) -> Self: "Axiom 1: ValueList and ReadTimeUnixMsList must have the same length." ) return self - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.multipurpose.telemetry.status.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.multipurpose.telemetry.status.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["TelemetryName"] - d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( - self.TelemetryName - ) - return d - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.multipurpose.telemetry.status.100 representation. - - Instances in the class are python-native representations of gt.sh.multipurpose.telemetry.status.100 - objects, while the actual gt.sh.multipurpose.telemetry.status.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShMultipurposeTelemetryStatus.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShMultipurposeTelemetryStatus_Maker: - type_name = "gt.sh.multipurpose.telemetry.status" - version = "100" - - def __init__( - self, - about_node_alias: str, - sensor_node_alias: str, - telemetry_name: EnumTelemetryName, - value_list: List[int], - read_time_unix_ms_list: List[int], - ) -> None: - self.tuple = GtShMultipurposeTelemetryStatus( - AboutNodeAlias=about_node_alias, - SensorNodeAlias=sensor_node_alias, - TelemetryName=telemetry_name, - ValueList=value_list, - ReadTimeUnixMsList=read_time_unix_ms_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShMultipurposeTelemetryStatus) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShMultipurposeTelemetryStatus: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShMultipurposeTelemetryStatus: - """ - Deserialize a dictionary representation of a gt.sh.multipurpose.telemetry.status.100 message object - into a GtShMultipurposeTelemetryStatus python object for internal use. - - This is the near-inverse of the GtShMultipurposeTelemetryStatus.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShMultipurposeTelemetryStatus object. - - Returns: - GtShMultipurposeTelemetryStatus - """ - d2 = dict(d) - if "AboutNodeAlias" not in d2: - raise SchemaError(f"dict missing AboutNodeAlias: <{d2}>") - if "SensorNodeAlias" not in d2: - raise SchemaError(f"dict missing SensorNodeAlias: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2: - raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") - value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) - d2["TelemetryName"] = EnumTelemetryName(value) - if "ValueList" not in d2: - raise SchemaError(f"dict missing ValueList: <{d2}>") - if "ReadTimeUnixMsList" not in d2: - raise SchemaError(f"dict missing ReadTimeUnixMsList: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret gt.sh.multipurpose.telemetry.status version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return GtShMultipurposeTelemetryStatus(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_sh_simple_telemetry_status.py b/src/gwproto/types/gt_sh_simple_telemetry_status.py index 9c5f25c9..9a5ace58 100644 --- a/src/gwproto/types/gt_sh_simple_telemetry_status.py +++ b/src/gwproto/types/gt_sh_simple_telemetry_status.py @@ -1,20 +1,11 @@ """Type gt.sh.simple.telemetry.status, version 100""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal, Self +from typing import List, Literal, Self -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel, model_validator -from gwproto.enums import TelemetryName as EnumTelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.enums.telemetry_name import TelemetryName +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds class GtShSimpleTelemetryStatus(BaseModel): @@ -29,52 +20,13 @@ class GtShSimpleTelemetryStatus(BaseModel): [More info](https://gridworks-protocol.readthedocs.io/en/latest/simple-sensor.html) """ - ShNodeAlias: str = Field( - title="SpaceheatNodeAlias", - description="The Alias of the SimpleSensor associated to the readings", - ) - TelemetryName: EnumTelemetryName = Field( - title="TelemetryName", - description=( - "The TelemetryName of the readings. This is used to interpet the meaning of the reading " - "values. For example, WaterTempCTimes1000 means the reading is measuring the temperature " - "of water, in Celsius multiplied by 1000. So a value of 37000 would be a reading " - "of 37 deg C." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/enums.html#gridworks-protocol.enums.TelemetryName)" - ), - ) - ValueList: List[int] = Field( - title="List of Values", - description="The values of the readings.", - ) - ReadTimeUnixMsList: List[int] = Field( - title="List of Read Times", - description="The times that the SImpleSensor took the readings, in unix milliseconds", - ) + ShNodeAlias: LeftRightDotStr + TelemetryName: TelemetryName + ValueList: List[int] + ReadTimeUnixMsList: List[UTCMilliseconds] TypeName: Literal["gt.sh.simple.telemetry.status"] = "gt.sh.simple.telemetry.status" Version: Literal["100"] = "100" - @field_validator("ShNodeAlias") - @classmethod - def _check_sh_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError(f"ShNodeAlias failed LeftRightDot format validation: {e}") - return v - - @field_validator("ReadTimeUnixMsList") - @classmethod - def _check_read_time_unix_ms_list(cls, v: List[int]) -> List[int]: - for elt in v: - try: - check_is_reasonable_unix_time_ms(elt) - except ValueError as e: # noqa: PERF203 - raise ValueError( - f"ReadTimeUnixMsList element {elt} failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - @model_validator(mode="after") def check_axiom_1(self) -> Self: """ @@ -87,189 +39,5 @@ def check_axiom_1(self) -> Self: ) return self - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.simple.telemetry.status.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.simple.telemetry.status.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["TelemetryName"] - d["TelemetryNameGtEnumSymbol"] = EnumTelemetryName.value_to_symbol( - self.TelemetryName - ) - return d - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.simple.telemetry.status.100 representation. - - Instances in the class are python-native representations of gt.sh.simple.telemetry.status.100 - objects, while the actual gt.sh.simple.telemetry.status.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShSimpleTelemetryStatus.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShSimpleTelemetryStatus_Maker: - type_name = "gt.sh.simple.telemetry.status" - version = "100" - - def __init__( - self, - sh_node_alias: str, - telemetry_name: EnumTelemetryName, - value_list: List[int], - read_time_unix_ms_list: List[int], - ) -> None: - self.tuple = GtShSimpleTelemetryStatus( - ShNodeAlias=sh_node_alias, - TelemetryName=telemetry_name, - ValueList=value_list, - ReadTimeUnixMsList=read_time_unix_ms_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShSimpleTelemetryStatus) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShSimpleTelemetryStatus: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShSimpleTelemetryStatus: - """ - Deserialize a dictionary representation of a gt.sh.simple.telemetry.status.100 message object - into a GtShSimpleTelemetryStatus python object for internal use. - - This is the near-inverse of the GtShSimpleTelemetryStatus.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShSimpleTelemetryStatus object. - - Returns: - GtShSimpleTelemetryStatus - """ - d2 = dict(d) - if "ShNodeAlias" not in d2: - raise SchemaError(f"dict missing ShNodeAlias: <{d2}>") - if "TelemetryNameGtEnumSymbol" not in d2: - raise SchemaError(f"TelemetryNameGtEnumSymbol missing from dict <{d2}>") - value = EnumTelemetryName.symbol_to_value(d2["TelemetryNameGtEnumSymbol"]) - d2["TelemetryName"] = EnumTelemetryName(value) - if "ValueList" not in d2: - raise SchemaError(f"dict missing ValueList: <{d2}>") - if "ReadTimeUnixMsList" not in d2: - raise SchemaError(f"dict missing ReadTimeUnixMsList: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret gt.sh.simple.telemetry.status version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return GtShSimpleTelemetryStatus(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") + return hash((type(self), *self.__dict__.values())) diff --git a/src/gwproto/types/gt_sh_status.py b/src/gwproto/types/gt_sh_status.py index 1099f822..2ca56f39 100644 --- a/src/gwproto/types/gt_sh_status.py +++ b/src/gwproto/types/gt_sh_status.py @@ -1,404 +1,37 @@ """Type gt.sh.status, version 110""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal +from typing import List, Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.errors import SchemaError +from gwproto.property_format import LeftRightDotStr, UTCSeconds, UUID4Str from gwproto.types.gt_sh_booleanactuator_cmd_status import ( GtShBooleanactuatorCmdStatus, - GtShBooleanactuatorCmdStatus_Maker, ) from gwproto.types.gt_sh_multipurpose_telemetry_status import ( GtShMultipurposeTelemetryStatus, - GtShMultipurposeTelemetryStatus_Maker, ) from gwproto.types.gt_sh_simple_telemetry_status import ( GtShSimpleTelemetryStatus, - GtShSimpleTelemetryStatus_Maker, ) -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - class GtShStatus(BaseModel): """ Status message sent by a Spaceheat SCADA every 5 minutes """ - FromGNodeAlias: str = Field( - title="FromGNodeAlias", - ) - FromGNodeId: str = Field( - title="FromGNodeId", - ) - AboutGNodeAlias: str = Field( - title="AboutGNodeAlias", - ) - SlotStartUnixS: int = Field( - title="SlotStartUnixS", - ) - ReportingPeriodS: int = Field( - title="ReportingPeriodS", - ) - SimpleTelemetryList: List[GtShSimpleTelemetryStatus] = Field( - title="SimpleTelemetryList", - ) - MultipurposeTelemetryList: List[GtShMultipurposeTelemetryStatus] = Field( - title="MultipurposeTelemetryList", - ) - BooleanactuatorCmdList: List[GtShBooleanactuatorCmdStatus] = Field( - title="BooleanactuatorCmdList", - ) - StatusUid: str = Field( - title="StatusUid", - ) + FromGNodeAlias: LeftRightDotStr + FromGNodeId: UUID4Str + AboutGNodeAlias: LeftRightDotStr + SlotStartUnixS: UTCSeconds + ReportingPeriodS: int + SimpleTelemetryList: list[GtShSimpleTelemetryStatus] + MultipurposeTelemetryList: list[GtShMultipurposeTelemetryStatus] + BooleanactuatorCmdList: List[GtShBooleanactuatorCmdStatus] + StatusUid: UUID4Str TypeName: Literal["gt.sh.status"] = "gt.sh.status" Version: Literal["110"] = "110" - @field_validator("FromGNodeAlias") - @classmethod - def _check_from_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"FromGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromGNodeId") - @classmethod - def _check_from_g_node_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"FromGNodeId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("AboutGNodeAlias") - @classmethod - def _check_about_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"AboutGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("SlotStartUnixS") - @classmethod - def _check_slot_start_unix_s(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_s(v) - except ValueError as e: - raise ValueError( - f"SlotStartUnixS failed ReasonableUnixTimeS format validation: {e}" - ) - return v - - @field_validator("StatusUid") - @classmethod - def _check_status_uid(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"StatusUid failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.status.110 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.status.110 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - # Recursively calling as_dict() - d["SimpleTelemetryList"] = [elt.as_dict() for elt in self.SimpleTelemetryList] - # Recursively calling as_dict() - d["MultipurposeTelemetryList"] = [ - elt.as_dict() for elt in self.MultipurposeTelemetryList - ] - # Recursively calling as_dict() - d["BooleanactuatorCmdList"] = [ - elt.as_dict() for elt in self.BooleanactuatorCmdList - ] - return d - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.status.110 representation. - - Instances in the class are python-native representations of gt.sh.status.110 - objects, while the actual gt.sh.status.110 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShStatus.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShStatus_Maker: - type_name = "gt.sh.status" - version = "110" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - from_g_node_alias: str, - from_g_node_id: str, - about_g_node_alias: str, - slot_start_unix_s: int, - reporting_period_s: int, - simple_telemetry_list: List[GtShSimpleTelemetryStatus], - multipurpose_telemetry_list: List[GtShMultipurposeTelemetryStatus], - booleanactuator_cmd_list: List[GtShBooleanactuatorCmdStatus], - status_uid: str, - ) -> None: - self.tuple = GtShStatus( - FromGNodeAlias=from_g_node_alias, - FromGNodeId=from_g_node_id, - AboutGNodeAlias=about_g_node_alias, - SlotStartUnixS=slot_start_unix_s, - ReportingPeriodS=reporting_period_s, - SimpleTelemetryList=simple_telemetry_list, - MultipurposeTelemetryList=multipurpose_telemetry_list, - BooleanactuatorCmdList=booleanactuator_cmd_list, - StatusUid=status_uid, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShStatus) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShStatus: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShStatus: # noqa: C901, PLR0912, PLR0915 - """ - Deserialize a dictionary representation of a gt.sh.status.110 message object - into a GtShStatus python object for internal use. - - This is the near-inverse of the GtShStatus.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShStatus object. - - Returns: - GtShStatus - """ - d2 = dict(d) - if "FromGNodeAlias" not in d2: - raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeId" not in d2: - raise SchemaError(f"dict missing FromGNodeId: <{d2}>") - if "AboutGNodeAlias" not in d2: - raise SchemaError(f"dict missing AboutGNodeAlias: <{d2}>") - if "SlotStartUnixS" not in d2: - raise SchemaError(f"dict missing SlotStartUnixS: <{d2}>") - if "ReportingPeriodS" not in d2: - raise SchemaError(f"dict missing ReportingPeriodS: <{d2}>") - if "SimpleTelemetryList" not in d2: - raise SchemaError(f"dict missing SimpleTelemetryList: <{d2}>") - if not isinstance(d2["SimpleTelemetryList"], List): - raise SchemaError( - f"SimpleTelemetryList <{d2['SimpleTelemetryList']}> must be a List!" - ) - simple_telemetry_list = [] - for elt in d2["SimpleTelemetryList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"SimpleTelemetryList <{d2['SimpleTelemetryList']}> must be a List of GtShSimpleTelemetryStatus types" - ) - t = GtShSimpleTelemetryStatus_Maker.dict_to_tuple(elt) - simple_telemetry_list.append(t) - d2["SimpleTelemetryList"] = simple_telemetry_list - if "MultipurposeTelemetryList" not in d2: - raise SchemaError(f"dict missing MultipurposeTelemetryList: <{d2}>") - if not isinstance(d2["MultipurposeTelemetryList"], List): - raise SchemaError( - f"MultipurposeTelemetryList <{d2['MultipurposeTelemetryList']}> must be a List!" - ) - multipurpose_telemetry_list = [] - for elt in d2["MultipurposeTelemetryList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"MultipurposeTelemetryList <{d2['MultipurposeTelemetryList']}> must be a List of GtShMultipurposeTelemetryStatus types" - ) - t = GtShMultipurposeTelemetryStatus_Maker.dict_to_tuple(elt) - multipurpose_telemetry_list.append(t) - d2["MultipurposeTelemetryList"] = multipurpose_telemetry_list - if "BooleanactuatorCmdList" not in d2: - raise SchemaError(f"dict missing BooleanactuatorCmdList: <{d2}>") - if not isinstance(d2["BooleanactuatorCmdList"], List): - raise SchemaError( - f"BooleanactuatorCmdList <{d2['BooleanactuatorCmdList']}> must be a List!" - ) - booleanactuator_cmd_list = [] - for elt in d2["BooleanactuatorCmdList"]: - if not isinstance(elt, dict): - raise SchemaError( - f"BooleanactuatorCmdList <{d2['BooleanactuatorCmdList']}> must be a List of GtShBooleanactuatorCmdStatus types" - ) - t = GtShBooleanactuatorCmdStatus_Maker.dict_to_tuple(elt) - booleanactuator_cmd_list.append(t) - d2["BooleanactuatorCmdList"] = booleanactuator_cmd_list - if "StatusUid" not in d2: - raise SchemaError(f"dict missing StatusUid: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "110": - LOGGER.debug( - f"Attempting to interpret gt.sh.status version {d2['Version']} as version 110" - ) - d2["Version"] = "110" - return GtShStatus(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_s(v: int) -> None: - """Checks ReasonableUnixTimeS format - - ReasonableUnixTimeS format: unix seconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeS format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + return hash(self.StatusUid) diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index ef640b26..7ad1d272 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -1,79 +1,23 @@ """Type gt.sh.telemetry.from.multipurpose.sensor, version 100""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal, Self +from typing import List, Literal, Self -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel, model_validator from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds class GtShTelemetryFromMultipurposeSensor(BaseModel): - """ - Data sent from a MultipurposeSensor to a Spaceheat SCADA. - - A set of readings made at the same time by a multipurpose sensor, sent by the MultipurposeSensor - SpaceheatNode actor to its SCADA. The nth element of each of its three readings (what is - getting read, what the value is, what the TelemetryNames are). - - [More info](https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html) - """ - - ScadaReadTimeUnixMs: int = Field( - title="ScadaReadTime in Unix MilliSeconds", - ) - AboutNodeAliasList: List[str] = Field( - title="AboutNodeAliasList", - description="List of aliases of the SpaceHeat Nodes getting measured", - ) - TelemetryNameList: List[TelemetryName] = Field( - title="TelemetryNameList", - description=( - "List of the TelemetryNames. The nth name in this list indicates the TelemetryName " - "of the nth alias in the AboutNodeAliasList." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/enums.html#gridworks-protocol.enums.TelemetryName)" - ), - ) - ValueList: List[int] = Field( - title="ValueList", - ) + ScadaReadTimeUnixMs: UTCMilliseconds + AboutNodeAliasList: list[LeftRightDotStr] + TelemetryNameList: List[TelemetryName] + ValueList: List[int] TypeName: Literal["gt.sh.telemetry.from.multipurpose.sensor"] = ( "gt.sh.telemetry.from.multipurpose.sensor" ) Version: Literal["100"] = "100" - @field_validator("ScadaReadTimeUnixMs") - @classmethod - def _check_scada_read_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"ScadaReadTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - @field_validator("AboutNodeAliasList") - @classmethod - def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: - for elt in v: - try: - check_is_left_right_dot(elt) - except ValueError as e: # noqa: PERF203 - raise ValueError( - f"AboutNodeAliasList element {elt} failed LeftRightDot format validation: {e}" - ) - return v - @model_validator(mode="after") def check_axiom_1(self) -> Self: """ @@ -89,195 +33,3 @@ def check_axiom_1(self) -> Self: "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." ) return self - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.sh.telemetry.from.multipurpose.sensor.100 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.sh.telemetry.from.multipurpose.sensor.100 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - d["TelemetryNameList"] = [ - TelemetryName.value_to_symbol(str(elt.value)) - for elt in self.TelemetryNameList - ] - return d - - def as_type(self) -> bytes: - """ - Serialize to the gt.sh.telemetry.from.multipurpose.sensor.100 representation. - - Instances in the class are python-native representations of gt.sh.telemetry.from.multipurpose.sensor.100 - objects, while the actual gt.sh.telemetry.from.multipurpose.sensor.100 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtShTelemetryFromMultipurposeSensor.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtShTelemetryFromMultipurposeSensor_Maker: - type_name = "gt.sh.telemetry.from.multipurpose.sensor" - version = "100" - - def __init__( - self, - scada_read_time_unix_ms: int, - about_node_alias_list: List[str], - telemetry_name_list: List[TelemetryName], - value_list: List[int], - ) -> None: - self.tuple = GtShTelemetryFromMultipurposeSensor( - ScadaReadTimeUnixMs=scada_read_time_unix_ms, - AboutNodeAliasList=about_node_alias_list, - TelemetryNameList=telemetry_name_list, - ValueList=value_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtShTelemetryFromMultipurposeSensor) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtShTelemetryFromMultipurposeSensor: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtShTelemetryFromMultipurposeSensor: - """ - Deserialize a dictionary representation of a gt.sh.telemetry.from.multipurpose.sensor.100 message object - into a GtShTelemetryFromMultipurposeSensor python object for internal use. - - This is the near-inverse of the GtShTelemetryFromMultipurposeSensor.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtShTelemetryFromMultipurposeSensor object. - - Returns: - GtShTelemetryFromMultipurposeSensor - """ - d2 = dict(d) - if "ScadaReadTimeUnixMs" not in d2: - raise SchemaError(f"dict missing ScadaReadTimeUnixMs: <{d2}>") - if "AboutNodeAliasList" not in d2: - raise SchemaError(f"dict missing AboutNodeAliasList: <{d2}>") - if "TelemetryNameList" not in d2: - raise SchemaError(f"dict <{d2}> missing TelemetryNameList") - if not isinstance(d2["TelemetryNameList"], List): - raise SchemaError("TelemetryNameList must be a List!") - telemetry_name_list = [] - for elt in d2["TelemetryNameList"]: - value = TelemetryName.symbol_to_value(elt) - telemetry_name_list.append(TelemetryName(value)) - d2["TelemetryNameList"] = telemetry_name_list - if "ValueList" not in d2: - raise SchemaError(f"dict missing ValueList: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "100": - LOGGER.debug( - f"Attempting to interpret gt.sh.telemetry.from.multipurpose.sensor version {d2['Version']} as version 100" - ) - d2["Version"] = "100" - return GtShTelemetryFromMultipurposeSensor(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py index 204bc0ad..f07ed588 100644 --- a/src/gwproto/types/gt_telemetry.py +++ b/src/gwproto/types/gt_telemetry.py @@ -1,221 +1,20 @@ """Type gt.telemetry, version 110""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import UTCMilliseconds class GtTelemetry(BaseModel): - """ - Data sent from a SimpleSensor to a SCADA. - - This type is meant to be used by a SimpleSensor, where _what_ is doing the reading can be - conflated with _what_ is being read. - """ - - ScadaReadTimeUnixMs: int = Field( - title="Scada Read Time in Unix Milliseconds", - ) - Value: int = Field( - title="Value", - description="The value of the reading.", - ) - Name: TelemetryName = Field( - title="Name", - description=( - "The name of the Simple Sensing Spaceheat Node. This is both the AboutNodeName and " - "FromNodeName for a data channel. The TelemetryName (and thus Units) are expected " - "to be inferred by the Spaceheat Node. For example this is done initially in SCADA " - "code according to whether the component of the Node is a PipeFlowSensorComponent, " - "SimpleTempSensorComponent etc." - ), - ) - Exponent: int = Field( - title="Exponent", - description=( - "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. " - "To match the implication in the name, the Exponent should be 3, and a Value of 65300 " - "would indicate 65.3 deg C" - ), - ) + ScadaReadTimeUnixMs: UTCMilliseconds + Value: int + Name: TelemetryName + Exponent: int TypeName: Literal["gt.telemetry"] = "gt.telemetry" Version: Literal["110"] = "110" - @field_validator("ScadaReadTimeUnixMs") - @classmethod - def _check_scada_read_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"ScadaReadTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - gt.telemetry.110 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - gt.telemetry.110 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - del d["Name"] - d["NameGtEnumSymbol"] = TelemetryName.value_to_symbol(self.Name) - return d - - def as_type(self) -> bytes: - """ - Serialize to the gt.telemetry.110 representation. - - Instances in the class are python-native representations of gt.telemetry.110 - objects, while the actual gt.telemetry.110 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is GtTelemetry.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class GtTelemetry_Maker: - type_name = "gt.telemetry" - version = "110" - - def __init__( - self, - scada_read_time_unix_ms: int, - value: int, - name: TelemetryName, - exponent: int, - ) -> None: - self.tuple = GtTelemetry( - ScadaReadTimeUnixMs=scada_read_time_unix_ms, - Value=value, - Name=name, - Exponent=exponent, - ) - - @classmethod - def tuple_to_type(cls, tpl: GtTelemetry) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> GtTelemetry: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> GtTelemetry: - """ - Deserialize a dictionary representation of a gt.telemetry.110 message object - into a GtTelemetry python object for internal use. - - This is the near-inverse of the GtTelemetry.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a GtTelemetry object. - - Returns: - GtTelemetry - """ - d2 = dict(d) - if "ScadaReadTimeUnixMs" not in d2: - raise SchemaError(f"dict missing ScadaReadTimeUnixMs: <{d2}>") - if "Value" not in d2: - raise SchemaError(f"dict missing Value: <{d2}>") - if "NameGtEnumSymbol" not in d2: - raise SchemaError(f"NameGtEnumSymbol missing from dict <{d2}>") - value = TelemetryName.symbol_to_value(d2["NameGtEnumSymbol"]) - d2["Name"] = TelemetryName(value) - if "Exponent" not in d2: - raise SchemaError(f"dict missing Exponent: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "110": - LOGGER.debug( - f"Attempting to interpret gt.telemetry version {d2['Version']} as version 110" - ) - d2["Version"] = "110" - return GtTelemetry(**d2) - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") + return hash((type(self), *self.__dict__.values())) diff --git a/src/gwproto/types/heartbeat_b.py b/src/gwproto/types/heartbeat_b.py index ed82c238..9f6bce69 100644 --- a/src/gwproto/types/heartbeat_b.py +++ b/src/gwproto/types/heartbeat_b.py @@ -1,48 +1,26 @@ """Type heartbeat.b, version 001""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import HexChar, LeftRightDotStr, UTCMilliseconds, UUID4Str class HeartbeatB(BaseModel): - """ - Heartbeat B. - - This is the Heartbeat intended to be sent between the Scada and the AtomicTNode to allow - for block-chain validation of the status of their communication. - - [More info](https://gridworks.readthedocs.io/en/latest/dispatch-contract.html) - """ - - FromGNodeAlias: str = Field( - title="My GNodeAlias", - ) - FromGNodeInstanceId: str = Field( - title="My GNodeInstanceId", - ) - MyHex: str = Field( + FromGNodeAlias: LeftRightDotStr + FromGNodeInstanceId: UUID4Str + MyHex: HexChar = Field( title="Hex character getting sent", default="0", ) - YourLastHex: str = Field( + YourLastHex: HexChar = Field( title="Last hex character received from heartbeat partner.", ) - LastReceivedTimeUnixMs: int = Field( + LastReceivedTimeUnixMs: UTCMilliseconds = Field( title="Time YourLastHex was received on my clock", ) - SendTimeUnixMs: int = Field( + SendTimeUnixMs: UTCMilliseconds = Field( title="Time this message is made and sent on my clock", ) StartingOver: bool = Field( @@ -56,312 +34,5 @@ class HeartbeatB(BaseModel): TypeName: Literal["heartbeat.b"] = "heartbeat.b" Version: Literal["001"] = "001" - @field_validator("FromGNodeAlias") - @classmethod - def _check_from_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"FromGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromGNodeInstanceId") - @classmethod - def _check_from_g_node_instance_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"FromGNodeInstanceId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("MyHex") - @classmethod - def _check_my_hex(cls, v: str) -> str: - try: - check_is_hex_char(v) - except ValueError as e: - raise ValueError(f"MyHex failed HexChar format validation: {e}") - return v - - @field_validator("YourLastHex") - @classmethod - def _check_your_last_hex(cls, v: str) -> str: - try: - check_is_hex_char(v) - except ValueError as e: - raise ValueError( - f"YourLastHex failed HexChar format validation: {e}" - ) from e - return v - - @field_validator("LastReceivedTimeUnixMs") - @classmethod - def _check_last_received_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"LastReceivedTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - @field_validator("SendTimeUnixMs") - @classmethod - def _check_send_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"SendTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - heartbeat.b.001 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - heartbeat.b.001 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the heartbeat.b.001 representation. - - Instances in the class are python-native representations of heartbeat.b.001 - objects, while the actual heartbeat.b.001 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is HeartbeatB.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class HeartbeatB_Maker: - type_name = "heartbeat.b" - version = "001" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - from_g_node_alias: str, - from_g_node_instance_id: str, - my_hex: str, - your_last_hex: str, - last_received_time_unix_ms: int, - send_time_unix_ms: int, - starting_over: bool, # noqa: FBT001 - ) -> None: - self.tuple = HeartbeatB( - FromGNodeAlias=from_g_node_alias, - FromGNodeInstanceId=from_g_node_instance_id, - MyHex=my_hex, - YourLastHex=your_last_hex, - LastReceivedTimeUnixMs=last_received_time_unix_ms, - SendTimeUnixMs=send_time_unix_ms, - StartingOver=starting_over, - ) - - @classmethod - def tuple_to_type(cls, tpl: HeartbeatB) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> HeartbeatB: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> HeartbeatB: # noqa: C901 - """ - Deserialize a dictionary representation of a heartbeat.b.001 message object - into a HeartbeatB python object for internal use. - - This is the near-inverse of the HeartbeatB.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a HeartbeatB object. - - Returns: - HeartbeatB - """ - d2 = dict(d) - if "FromGNodeAlias" not in d2: - raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2: - raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "MyHex" not in d2: - raise SchemaError(f"dict missing MyHex: <{d2}>") - if "YourLastHex" not in d2: - raise SchemaError(f"dict missing YourLastHex: <{d2}>") - if "LastReceivedTimeUnixMs" not in d2: - raise SchemaError(f"dict missing LastReceivedTimeUnixMs: <{d2}>") - if "SendTimeUnixMs" not in d2: - raise SchemaError(f"dict missing SendTimeUnixMs: <{d2}>") - if "StartingOver" not in d2: - raise SchemaError(f"dict missing StartingOver: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "001": - LOGGER.debug( - f"Attempting to interpret heartbeat.b version {d2['Version']} as version 001" - ) - d2["Version"] = "001" - return HeartbeatB(**d2) - - -def check_is_hex_char(v: str) -> None: - """Checks HexChar format - - HexChar format: single-char string in '0123456789abcdefABCDEF' - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not HexChar format - """ - if not isinstance(v, str): - raise ValueError(f"<{v}> must be a hex char, but not even a string") # noqa: TRY004 - if len(v) > 1: - raise ValueError(f"<{v}> must be a hex char, but not of len 1") - if v not in "0123456789abcdefABCDEF": - raise ValueError(f"<{v}> must be one of '0123456789abcdefABCDEF'") - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") + return hash((type(self), *self.__dict__.values())) diff --git a/src/gwproto/types/power_watts.py b/src/gwproto/types/power_watts.py index 8c237f89..a2ac1846 100644 --- a/src/gwproto/types/power_watts.py +++ b/src/gwproto/types/power_watts.py @@ -1,18 +1,8 @@ """Type power.watts, version 000""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field - -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from pydantic import BaseModel class PowerWatts(BaseModel): @@ -26,130 +16,6 @@ class PowerWatts(BaseModel): bytes so comes in JSON format. """ - Watts: int = Field( - title="Current Power in Watts", - ) + Watts: int TypeName: Literal["power.watts"] = "power.watts" Version: Literal["000"] = "000" - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - power.watts.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - power.watts.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - return { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - - def as_type(self) -> bytes: - """ - Serialize to the power.watts.000 representation. - - Instances in the class are python-native representations of power.watts.000 - objects, while the actual power.watts.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is PowerWatts.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class PowerWatts_Maker: - type_name = "power.watts" - version = "000" - - def __init__( - self, - watts: int, - ) -> None: - self.tuple = PowerWatts( - Watts=watts, - ) - - @classmethod - def tuple_to_type(cls, tpl: PowerWatts) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> PowerWatts: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> PowerWatts: - """ - Deserialize a dictionary representation of a power.watts.000 message object - into a PowerWatts python object for internal use. - - This is the near-inverse of the PowerWatts.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a PowerWatts object. - - Returns: - PowerWatts - """ - d2 = dict(d) - if "Watts" not in d2: - raise SchemaError(f"dict missing Watts: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret power.watts version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return PowerWatts(**d2) diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 910390e6..3bbb3134 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -1,259 +1,18 @@ """Type snapshot.spaceheat, version 000""" -import json -import logging -from typing import Any, Dict, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.errors import SchemaError +from gwproto.property_format import LeftRightDotStr, UUID4Str from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, - TelemetrySnapshotSpaceheat_Maker, ) -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) - class SnapshotSpaceheat(BaseModel): - """ """ - - FromGNodeAlias: str = Field( - title="FromGNodeAlias", - ) - FromGNodeInstanceId: str = Field( - title="FromGNodeInstanceId", - ) - Snapshot: TelemetrySnapshotSpaceheat = Field( - title="Snapshot", - ) + FromGNodeAlias: LeftRightDotStr + FromGNodeInstanceId: UUID4Str + Snapshot: TelemetrySnapshotSpaceheat TypeName: Literal["snapshot.spaceheat"] = "snapshot.spaceheat" Version: Literal["000"] = "000" - - @field_validator("FromGNodeAlias") - @classmethod - def _check_from_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"FromGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("FromGNodeInstanceId") - @classmethod - def _check_from_g_node_instance_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"FromGNodeInstanceId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - snapshot.spaceheat.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - snapshot.spaceheat.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - d["Snapshot"] = self.Snapshot.as_dict() - return d - - def as_type(self) -> bytes: - """ - Serialize to the snapshot.spaceheat.000 representation. - - Instances in the class are python-native representations of snapshot.spaceheat.000 - objects, while the actual snapshot.spaceheat.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is SnapshotSpaceheat.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class SnapshotSpaceheat_Maker: - type_name = "snapshot.spaceheat" - version = "000" - - def __init__( - self, - from_g_node_alias: str, - from_g_node_instance_id: str, - snapshot: TelemetrySnapshotSpaceheat, - ) -> None: - self.tuple = SnapshotSpaceheat( - FromGNodeAlias=from_g_node_alias, - FromGNodeInstanceId=from_g_node_instance_id, - Snapshot=snapshot, - ) - - @classmethod - def tuple_to_type(cls, tpl: SnapshotSpaceheat) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> SnapshotSpaceheat: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> SnapshotSpaceheat: - """ - Deserialize a dictionary representation of a snapshot.spaceheat.000 message object - into a SnapshotSpaceheat python object for internal use. - - This is the near-inverse of the SnapshotSpaceheat.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a SnapshotSpaceheat object. - - Returns: - SnapshotSpaceheat - """ - d2 = dict(d) - if "FromGNodeAlias" not in d2: - raise SchemaError(f"dict missing FromGNodeAlias: <{d2}>") - if "FromGNodeInstanceId" not in d2: - raise SchemaError(f"dict missing FromGNodeInstanceId: <{d2}>") - if "Snapshot" not in d2: - raise SchemaError(f"dict missing Snapshot: <{d2}>") - if not isinstance(d2["Snapshot"], dict): - raise SchemaError( - f"Snapshot <{d2['Snapshot']}> must be a TelemetrySnapshotSpaceheat!" - ) - snapshot = TelemetrySnapshotSpaceheat_Maker.dict_to_tuple(d2["Snapshot"]) - d2["Snapshot"] = snapshot - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret snapshot.spaceheat version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return SnapshotSpaceheat(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: list[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py index 55d3a8c5..65027a6f 100644 --- a/src/gwproto/types/ta_data_channels.py +++ b/src/gwproto/types/ta_data_channels.py @@ -1,337 +1,19 @@ """Type ta.data.channels, version 000""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal +from typing import Literal -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -from gwproto.errors import SchemaError -from gwproto.types.data_channel import DataChannel, DataChannel_Maker - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCSeconds, UUID4Str +from gwproto.types.data_channel import DataChannel class TaDataChannels(BaseModel): - """ - Terminal Asset Data Channels. - - A list of data channels associated to a specific Terminal Asset. - """ - - TerminalAssetGNodeAlias: str = Field( - title="GNodeAlias for the Terminal Asset", - description=( - "The Alias of the Terminal Asset about which the time series data is providing information." - ), - ) - TerminalAssetGNodeId: str = Field( - title="GNodeId for the Terminal Asset", - description="The immutable unique identifier for the Terminal Asset.", - ) - TimeUnixS: int = Field( - title="TimeUnixS", - description="The time that this list of data channels was created", - ) - Author: str = Field( - title="Author", - description="Author of this list of data channels.", - ) - Channels: List[DataChannel] = Field( - title="The list of data channels", - ) - Identifier: str = Field( - title="Identifier", - description=( - "Unique identifier for a specific instance of this type that can be used to establish " - "how time series csv's were constructed." - ), - ) + TerminalAssetGNodeAlias: LeftRightDotStr + TerminalAssetGNodeId: UUID4Str + TimeUnixS: UTCSeconds + Author: str + Channels: list[DataChannel] + Identifier: UUID4Str TypeName: Literal["ta.data.channels"] = "ta.data.channels" Version: Literal["000"] = "000" - - @field_validator("TerminalAssetGNodeAlias") - @classmethod - def _check_terminal_asset_g_node_alias(cls, v: str) -> str: - try: - check_is_left_right_dot(v) - except ValueError as e: - raise ValueError( - f"TerminalAssetGNodeAlias failed LeftRightDot format validation: {e}" - ) - return v - - @field_validator("TerminalAssetGNodeId") - @classmethod - def _check_terminal_asset_g_node_id(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"TerminalAssetGNodeId failed UuidCanonicalTextual format validation: {e}" - ) - return v - - @field_validator("TimeUnixS") - @classmethod - def _check_time_unix_s(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_s(v) - except ValueError as e: - raise ValueError( - f"TimeUnixS failed ReasonableUnixTimeS format validation: {e}" - ) - return v - - @field_validator("Identifier") - @classmethod - def _check_identifier(cls, v: str) -> str: - try: - check_is_uuid_canonical_textual(v) - except ValueError as e: - raise ValueError( - f"Identifier failed UuidCanonicalTextual format validation: {e}" - ) - return v - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - ta.data.channels.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - ta.data.channels.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - # Recursively calling as_dict() - d["Channels"] = [elt.as_dict() for elt in self.Channels] - return d - - def as_type(self) -> bytes: - """ - Serialize to the ta.data.channels.000 representation. - - Instances in the class are python-native representations of ta.data.channels.000 - objects, while the actual ta.data.channels.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is TaDataChannels.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class TaDataChannels_Maker: - type_name = "ta.data.channels" - version = "000" - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - terminal_asset_g_node_alias: str, - terminal_asset_g_node_id: str, - time_unix_s: int, - author: str, - channels: List[DataChannel], - identifier: str, - ) -> None: - self.tuple = TaDataChannels( - TerminalAssetGNodeAlias=terminal_asset_g_node_alias, - TerminalAssetGNodeId=terminal_asset_g_node_id, - TimeUnixS=time_unix_s, - Author=author, - Channels=channels, - Identifier=identifier, - ) - - @classmethod - def tuple_to_type(cls, tpl: TaDataChannels) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> TaDataChannels: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> TaDataChannels: # noqa: C901 - """ - Deserialize a dictionary representation of a ta.data.channels.000 message object - into a TaDataChannels python object for internal use. - - This is the near-inverse of the TaDataChannels.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a TaDataChannels object. - - Returns: - TaDataChannels - """ - d2 = dict(d) - if "TerminalAssetGNodeAlias" not in d2: - raise SchemaError(f"dict missing TerminalAssetGNodeAlias: <{d2}>") - if "TerminalAssetGNodeId" not in d2: - raise SchemaError(f"dict missing TerminalAssetGNodeId: <{d2}>") - if "TimeUnixS" not in d2: - raise SchemaError(f"dict missing TimeUnixS: <{d2}>") - if "Author" not in d2: - raise SchemaError(f"dict missing Author: <{d2}>") - if "Channels" not in d2: - raise SchemaError(f"dict missing Channels: <{d2}>") - if not isinstance(d2["Channels"], List): - raise SchemaError(f"Channels <{d2['Channels']}> must be a List!") - channels = [] - for elt in d2["Channels"]: - if not isinstance(elt, dict): - raise SchemaError( - f"Channels <{d2['Channels']}> must be a List of DataChannel types" - ) - t = DataChannel_Maker.dict_to_tuple(elt) - channels.append(t) - d2["Channels"] = channels - if "Identifier" not in d2: - raise SchemaError(f"dict missing Identifier: <{d2}>") - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret ta.data.channels version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return TaDataChannels(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_s(v: int) -> None: - """Checks ReasonableUnixTimeS format - - ReasonableUnixTimeS format: unix seconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeS format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index 3bd4e0fd..e459796b 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -1,284 +1,17 @@ """Type telemetry.snapshot.spaceheat, version 000""" -import json -import logging -from datetime import datetime, timezone -from typing import Any, Dict, List, Literal, Self +from typing import List, Literal -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError - -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) +from gwproto.property_format import LeftRightDotStr, UTCMilliseconds class TelemetrySnapshotSpaceheat(BaseModel): - """ - Snapshot of Telemetry Data from a SpaceHeat SCADA. - - A snapshot of all current sensed states, sent from a spaceheat SCADA to its AtomicTNode. - The nth element of each of the three lists refer to the same reading (i.e., what is getting - read, what the value is, what the TelemetryNames are.) - - [More info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html) - """ - - ReportTimeUnixMs: int = Field( - title="ReportTimeUnixMs", - description=( - "The time, in unix ms, that the SCADA creates this type. It may not be when the SCADA " - "sends the type to the atn (for example if Internet is down)." - ), - ) - AboutNodeAliasList: List[str] = Field( - title="AboutNodeAliases", - description=( - "The list of Spaceheat nodes in the snapshot." - "[More info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html)" - ), - ) - ValueList: List[int] = Field( - title="ValueList", - ) - TelemetryNameList: List[TelemetryName] = Field( - title="TelemetryNameList", - description="[More info](https://gridworks-protocol.readthedocs.io/en/latest/telemetry-name.html)", - ) + ReportTimeUnixMs: UTCMilliseconds + AboutNodeAliasList: list[LeftRightDotStr] + ValueList: List[int] + TelemetryNameList: list[TelemetryName] TypeName: Literal["telemetry.snapshot.spaceheat"] = "telemetry.snapshot.spaceheat" Version: Literal["000"] = "000" - - @field_validator("ReportTimeUnixMs") - @classmethod - def _check_report_time_unix_ms(cls, v: int) -> int: - try: - check_is_reasonable_unix_time_ms(v) - except ValueError as e: - raise ValueError( - f"ReportTimeUnixMs failed ReasonableUnixTimeMs format validation: {e}" - ) - return v - - @field_validator("AboutNodeAliasList") - @classmethod - def _check_about_node_alias_list(cls, v: List[str]) -> List[str]: - for elt in v: - try: - check_is_left_right_dot(elt) - except ValueError as e: # noqa: PERF203 - raise ValueError( - f"AboutNodeAliasList element {elt} failed LeftRightDot format validation: {e}" - ) - return v - - @model_validator(mode="after") - def check_axiom_1(self) -> Self: - """ - Axiom 1: ListLengthConsistency. - AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length. - """ - if not ( - len(self.ValueList) - == len(self.AboutNodeAliasList) - == len(self.TelemetryNameList) - ): - raise ValueError( - "Axiom 1: AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." - ) - return self - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - telemetry.snapshot.spaceheat.000 object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - telemetry.snapshot.spaceheat.000 type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.model_dump( - include=self.model_fields_set | {"TypeName", "Version"} - ).items() - if value is not None - } - d["TelemetryNameList"] = [ - TelemetryName.value_to_symbol(str(elt.value)) - for elt in self.TelemetryNameList - ] - return d - - def as_type(self) -> bytes: - """ - Serialize to the telemetry.snapshot.spaceheat.000 representation. - - Instances in the class are python-native representations of telemetry.snapshot.spaceheat.000 - objects, while the actual telemetry.snapshot.spaceheat.000 object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is TelemetrySnapshotSpaceheat.type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self) -> int: - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class TelemetrySnapshotSpaceheat_Maker: - type_name = "telemetry.snapshot.spaceheat" - version = "000" - - def __init__( - self, - report_time_unix_ms: int, - about_node_alias_list: List[str], - value_list: List[int], - telemetry_name_list: List[TelemetryName], - ) -> None: - self.tuple = TelemetrySnapshotSpaceheat( - ReportTimeUnixMs=report_time_unix_ms, - AboutNodeAliasList=about_node_alias_list, - ValueList=value_list, - TelemetryNameList=telemetry_name_list, - ) - - @classmethod - def tuple_to_type(cls, tpl: TelemetrySnapshotSpaceheat) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> TelemetrySnapshotSpaceheat: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> TelemetrySnapshotSpaceheat: - """ - Deserialize a dictionary representation of a telemetry.snapshot.spaceheat.000 message object - into a TelemetrySnapshotSpaceheat python object for internal use. - - This is the near-inverse of the TelemetrySnapshotSpaceheat.as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a TelemetrySnapshotSpaceheat object. - - Returns: - TelemetrySnapshotSpaceheat - """ - d2 = dict(d) - if "ReportTimeUnixMs" not in d2: - raise SchemaError(f"dict missing ReportTimeUnixMs: <{d2}>") - if "AboutNodeAliasList" not in d2: - raise SchemaError(f"dict missing AboutNodeAliasList: <{d2}>") - if "ValueList" not in d2: - raise SchemaError(f"dict missing ValueList: <{d2}>") - if "TelemetryNameList" not in d2: - raise SchemaError(f"dict <{d2}> missing TelemetryNameList") - if not isinstance(d2["TelemetryNameList"], List): - raise SchemaError("TelemetryNameList must be a List!") - telemetry_name_list = [] - for elt in d2["TelemetryNameList"]: - value = TelemetryName.symbol_to_value(elt) - telemetry_name_list.append(TelemetryName(value)) - d2["TelemetryNameList"] = telemetry_name_list - if "TypeName" not in d2: - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2: - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "000": - LOGGER.debug( - f"Attempting to interpret telemetry.snapshot.spaceheat version {d2['Version']} as version 000" - ) - d2["Version"] = "000" - return TelemetrySnapshotSpaceheat(**d2) - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - try: - x: List[str] = v.split(".") - except Exception as e: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > v: - raise ValueError(f"<{v}> must be after Jan 1 2000") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < v: - raise ValueError(f"<{v}> must be before Jan 1 3000") diff --git a/tests/data/snapshot_message.json b/tests/data/snapshot_message.json index f322f9df..1563851b 100644 --- a/tests/data/snapshot_message.json +++ b/tests/data/snapshot_message.json @@ -31,7 +31,7 @@ ], "ReportTimeUnixMs": 1676592300011, "TypeName": "telemetry.snapshot.spaceheat", - "Version": "100", + "Version": "000", "TelemetryNameList": [ "5a71d4b3", "5a71d4b3", @@ -49,7 +49,7 @@ ] }, "TypeName": "snapshot.spaceheat", - "Version": "100" + "Version": "000" }, "TypeName": "gw" } diff --git a/tests/data/status_message.json b/tests/data/status_message.json index 9c0d6d09..c83d4c50 100644 --- a/tests/data/status_message.json +++ b/tests/data/status_message.json @@ -17,7 +17,7 @@ "AboutNodeAlias": "a.elt1", "ReadTimeUnixMsList": [1676592055586], "SensorNodeAlias": "a.m", - "TelemetryNameGtEnumSymbol": "af39eec9", + "TelemetryName": "PowerW", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [0] @@ -29,7 +29,7 @@ 1676592240089 ], "SensorNodeAlias": "a.s.analog.temp", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [77, 361, -115, -9, -44] @@ -41,7 +41,7 @@ 1676592240089 ], "SensorNodeAlias": "a.s.analog.temp", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [-3587, -3574, -3771, -3656, -3626] @@ -53,7 +53,7 @@ 1676592240089 ], "SensorNodeAlias": "a.s.analog.temp", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [-3222, -3036, -3264, -3087, -3015] @@ -65,7 +65,7 @@ 1676592240089 ], "SensorNodeAlias": "a.s.analog.temp", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", "ValueList": [3011, 3037, 2893, 3037, 3041] @@ -76,7 +76,7 @@ { "ReadTimeUnixMsList": [1676592052038], "ShNodeAlias": "a.elt1.relay", - "TelemetryNameGtEnumSymbol": "5a71d4b3", + "TelemetryName": "RelayState", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0] @@ -84,7 +84,7 @@ { "ReadTimeUnixMsList": [1676592232171], "ShNodeAlias": "a.tank.out.pump.relay", - "TelemetryNameGtEnumSymbol": "5a71d4b3", + "TelemetryName": "RelayState", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0] @@ -92,7 +92,7 @@ { "ReadTimeUnixMsList": [1676592079160], "ShNodeAlias": "a.tank.out.pump.baseboard1.fan.relay", - "TelemetryNameGtEnumSymbol": "5a71d4b3", + "TelemetryName": "RelayState", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [0] @@ -103,7 +103,7 @@ 1676592275685 ], "ShNodeAlias": "a.tank.out.temp1", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [18000, 17937, 17937, 17875, 17812] @@ -114,7 +114,7 @@ 1676592284085 ], "ShNodeAlias": "a.tank.out.far.temp1", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [15375, 15312, 15312, 15312, 15250] @@ -125,7 +125,7 @@ 1676592275192 ], "ShNodeAlias": "a.tank.in.temp1", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [14437, 14375, 14312, 14312, 14312] @@ -136,7 +136,7 @@ 1676592275380 ], "ShNodeAlias": "a.tank.temp0", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [21812, 21812, 21812, 21812, 21812] @@ -147,7 +147,7 @@ 1676592264435 ], "ShNodeAlias": "a.garage.temp1", - "TelemetryNameGtEnumSymbol": "c89d0ba1", + "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", "ValueList": [15125, 15062, 15062, 15062, 15062] diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py index 3bf6f003..02d13da4 100644 --- a/tests/data_classes/test_electric_meter_cac.py +++ b/tests/data_classes/test_electric_meter_cac.py @@ -1,26 +1,16 @@ -# from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac -# from gwproto.data_classes.hardware_layout import HardwareLayout -# from gwproto.types import ElectricMeterCacGt_Maker -# -# # Running the below disrupts other tests. Need to set up the -# # test isolation as per scada -# -# -# def test_electric_meter_cac() -> None: -# HardwareLayout.load("tests/config/hardware-layout.json") -# d = { -# "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", -# "MakeModelGtEnumSymbol": "076da322", -# "DisplayName": "Gridworks Pm1 Simulated Power Meter", -# "InterfaceGtEnumSymbol": "efc144cd", -# "PollPeriodMs": 1000, -# "TelemetryNameList": ["af39eec9"], -# "TypeName": "electric.meter.cac.gt", -# "Version": "000", -# } -# -# gw_tuple = ElectricMeterCacGt_Maker.dict_to_tuple(d) -# assert gw_tuple.ComponentAttributeClassId in ElectricMeterCac.by_id -# dc = ElectricMeterCac.by_id[gw_tuple.ComponentAttributeClassId] -# -# assert (repr(dc)) == "GRIDWORKS__SIMPM1 Gridworks Pm1 Simulated Power Meter" +from gwproto.types import ElectricMeterCacGt +from tests.cac_load_utils import CacCase, assert_cac_load + + +def test_electric_meter_cac() -> None: + d = { + "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", + "MakeModel": "GRIDWORKS__SIMPM1", + "DisplayName": "Gridworks Pm1 Simulated Power Meter", + "Interface": "SIMRABBIT", + "PollPeriodMs": 1000, + "TelemetryNameList": ["PowerW"], + "TypeName": "electric.meter.cac.gt", + "Version": "000", + } + assert_cac_load([CacCase("ElectricMeterCac", d, ElectricMeterCacGt)]) diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index a4813c35..9700aab7 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -1,5 +1,4 @@ from gwproto import Decoders, MQTTCodec, create_message_payload_discriminator -from gwproto.messages import GtDispatchBoolean_Maker, GtShCliAtnCmd_Maker from tests.dummy_decoders import PARENT ChildMessageDecoder = create_message_payload_discriminator( @@ -14,10 +13,6 @@ class ChildMQTTCodec(MQTTCodec): def __init__(self) -> None: super().__init__( Decoders.from_objects( - [ - GtDispatchBoolean_Maker, - GtShCliAtnCmd_Maker, - ], message_payload_discriminator=ChildMessageDecoder, ) ) diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index 81f016ad..e2d7513e 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -5,7 +5,6 @@ create_message_payload_discriminator, ) from gwproto.gs import GsPwr_Maker -from gwproto.messages import GtShStatus_Maker, SnapshotSpaceheat_Maker from tests.dummy_decoders import CHILD ParentMessageDecoder = create_message_payload_discriminator( @@ -18,10 +17,6 @@ class ParentMQTTCodec(MQTTCodec): def __init__(self) -> None: super().__init__( Decoders.from_objects( - [ - GtShStatus_Maker, - SnapshotSpaceheat_Maker, - ], message_payload_discriminator=ParentMessageDecoder, ).add_decoder( "p", CallableDecoder(lambda decoded: GsPwr_Maker(decoded[0]).tuple) diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 82348d26..379fff81 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -9,9 +9,6 @@ from gwproto.messages import ( Ack, AnyEvent, - GtDispatchBoolean_Maker, - GtShCliAtnCmd_Maker, - GtShStatus_Maker, GtShStatusEvent, MQTTConnectEvent, MQTTConnectFailedEvent, @@ -19,15 +16,20 @@ MQTTFullySubscribedEvent, PeerActiveEvent, PingMessage, - PowerWatts_Maker, ProblemEvent, Problems, ResponseTimeoutEvent, ShutdownEvent, - SnapshotSpaceheat_Maker, SnapshotSpaceheatEvent, StartupEvent, ) +from gwproto.types import ( + GtDispatchBoolean, + GtShCliAtnCmd, + GtShStatus, + PowerWatts, + SnapshotSpaceheat, +) from tests.dummy_decoders import CHILD, PARENT from tests.dummy_decoders.child.codec import ChildMQTTCodec from tests.dummy_decoders.parent.codec import ParentMQTTCodec @@ -53,7 +55,7 @@ def child_to_parent_payload_dicts() -> dict: def child_to_parent_messages() -> list[MessageCase]: stored_message_dicts = child_to_parent_payload_dicts() status_message_dict = stored_message_dicts["status"] - gt_sh_status = GtShStatus_Maker.dict_to_tuple(status_message_dict["Payload"]) + gt_sh_status = GtShStatus.model_validate(status_message_dict["Payload"]) gt_sh_status_event = GtShStatusEvent(Src=CHILD, status=gt_sh_status) unrecognized_status_event = AnyEvent(**gt_sh_status_event.model_dump()) unrecognized_status_event.TypeName += ".foo" @@ -68,18 +70,14 @@ def child_to_parent_messages() -> list[MessageCase]: ) unrecognizeable_bad_event_content = {"TypeName": "gridworks.event.baz"} snap_message_dict = stored_message_dicts["snapshot"] - snapshot_spaceheat = SnapshotSpaceheat_Maker.dict_to_tuple( - snap_message_dict["Payload"] - ) + snapshot_spaceheat = SnapshotSpaceheat.model_validate(snap_message_dict["Payload"]) snapshot_event = SnapshotSpaceheatEvent(Src=CHILD, snap=snapshot_spaceheat) return [ # Gs Pwr MessageCase( "GsPwr", - Message( - Src=CHILD, MessageType="power.watts", Payload=PowerWatts_Maker(1).tuple - ), + Message(Src=CHILD, MessageType="power.watts", Payload=PowerWatts(Watts=1)), ), # status # QUESTION: why does this fail when replacing "gt.sh.status.110" with "gt.sh.status"? @@ -97,7 +95,7 @@ def child_to_parent_messages() -> list[MessageCase]: ), MessageCase( "status-payload-as_dict", - Message(Src=CHILD, Payload=gt_sh_status.as_dict()), + Message(Src=CHILD, Payload=gt_sh_status), None, gt_sh_status, ), @@ -111,7 +109,7 @@ def child_to_parent_messages() -> list[MessageCase]: ), MessageCase( "snap-payload-as_dict", - Message(Src=CHILD, Payload=snapshot_spaceheat.as_dict()), + Message(Src=CHILD, Payload=snapshot_spaceheat), None, snapshot_spaceheat, ), @@ -183,32 +181,32 @@ def child_to_parent_messages() -> list[MessageCase]: def parent_to_child_messages() -> list[MessageCase]: - snapshot_request = GtShCliAtnCmd_Maker( - from_g_node_alias="a.b.c", - from_g_node_id=str(uuid.uuid4()), - send_snapshot=True, - ).tuple - set_relay = GtDispatchBoolean_Maker( - about_node_name="a.b.c", - to_g_node_alias="a.b.c", - from_g_node_alias="a.b.c", - from_g_node_instance_id=str(uuid.uuid4()), - relay_state=1, - send_time_unix_ms=int(time.time() * 1000), - ).tuple + snapshot_request = GtShCliAtnCmd( + FromGNodeAlias="a.b.c", + FromGNodeId=str(uuid.uuid4()), + SendSnapshot=True, + ) + set_relay = GtDispatchBoolean( + AboutNodeName="a.b.c", + ToGNodeAlias="a.b.c", + FromGNodeAlias="a.b.c", + FromGNodeInstanceId=str(uuid.uuid4()), + RelayState=True, + SendTimeUnixMs=int(time.time() * 1000), + ) return [ # misc messages MessageCase("ping", PingMessage(Src=PARENT)), MessageCase("ack", Message(Src=PARENT, Payload=Ack(AckMessageID="1"))), MessageCase( "snap", - Message(Src=PARENT, Payload=snapshot_request.as_dict()), + Message(Src=PARENT, Payload=snapshot_request), None, snapshot_request, ), MessageCase( "set-relay", - Message(Src=PARENT, Payload=set_relay.as_dict()), + Message(Src=PARENT, Payload=set_relay), None, set_relay, ), diff --git a/tests/types/test_data_channel.py b/tests/types/test_data_channel.py index 867de68d..1108a0d7 100644 --- a/tests/types/test_data_channel.py +++ b/tests/types/test_data_channel.py @@ -1,13 +1,6 @@ """Tests data.channel type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError -from gwproto.types import DataChannel_Maker as Maker +from gwproto.types import DataChannel def test_data_channel_generated() -> None: @@ -15,87 +8,8 @@ def test_data_channel_generated() -> None: "DisplayName": "BoostPower", "AboutName": "a.elt1", "CapturedByName": "a.m", - "TelemetryNameGtEnumSymbol": "af39eec9", + "TelemetryName": "PowerW", "TypeName": "data.channel", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - display_name=gtuple.DisplayName, - about_name=gtuple.AboutName, - captured_by_name=gtuple.CapturedByName, - telemetry_name=gtuple.TelemetryName, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["DisplayName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["CapturedByName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, AboutName="A.hot-stuff") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, CapturedByName="A.hot-stuff") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert DataChannel.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py index cd659806..5bdfec99 100644 --- a/tests/types/test_gt_dispatch_boolean.py +++ b/tests/types/test_gt_dispatch_boolean.py @@ -1,12 +1,6 @@ """Tests gt.dispatch.boolean type, version 110""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtDispatchBoolean_Maker as Maker +from gwproto.types import GtDispatchBoolean def test_gt_dispatch_boolean_generated() -> None: @@ -20,116 +14,5 @@ def test_gt_dispatch_boolean_generated() -> None: "TypeName": "gt.dispatch.boolean", "Version": "110", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - about_node_name=gtuple.AboutNodeName, - to_g_node_alias=gtuple.ToGNodeAlias, - from_g_node_alias=gtuple.FromGNodeAlias, - from_g_node_instance_id=gtuple.FromGNodeInstanceId, - relay_state=gtuple.RelayState, - send_time_unix_ms=gtuple.SendTimeUnixMs, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ToGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeInstanceId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RelayState"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SendTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, RelayState="0.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs="1657024737661.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, AboutNodeName="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ToGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeInstanceId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, RelayState=2) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + payload = GtDispatchBoolean.model_validate(d) + assert payload.model_dump() == d diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py index 2371adce..e241d403 100644 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ b/tests/types/test_gt_dispatch_boolean_local.py @@ -1,12 +1,6 @@ """Tests gt.dispatch.boolean.local type, version 110""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtDispatchBooleanLocal_Maker as Maker +from gwproto.types import GtDispatchBooleanLocal def test_gt_dispatch_boolean_local_generated() -> None: @@ -18,96 +12,5 @@ def test_gt_dispatch_boolean_local_generated() -> None: "TypeName": "gt.dispatch.boolean.local", "Version": "110", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - relay_state=gtuple.RelayState, - about_node_name=gtuple.AboutNodeName, - from_node_name=gtuple.FromNodeName, - send_time_unix_ms=gtuple.SendTimeUnixMs, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RelayState"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromNodeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SendTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, RelayState="1.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs="1657025211851.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, RelayState=2) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, AboutNodeName="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromNodeName="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + payload = GtDispatchBooleanLocal.model_validate(d) + assert payload.model_dump() == d diff --git a/tests/types/test_gt_driver_booleanactuator_cmd.py b/tests/types/test_gt_driver_booleanactuator_cmd.py index 8568830e..aba9f380 100644 --- a/tests/types/test_gt_driver_booleanactuator_cmd.py +++ b/tests/types/test_gt_driver_booleanactuator_cmd.py @@ -1,12 +1,6 @@ """Tests gt.driver.booleanactuator.cmd type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtDriverBooleanactuatorCmd_Maker as Maker +from gwproto.types import GtDriverBooleanactuatorCmd def test_gt_driver_booleanactuator_cmd_generated() -> None: @@ -17,86 +11,5 @@ def test_gt_driver_booleanactuator_cmd_generated() -> None: "TypeName": "gt.driver.booleanactuator.cmd", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - relay_state=gtuple.RelayState, - sh_node_alias=gtuple.ShNodeAlias, - command_time_unix_ms=gtuple.CommandTimeUnixMs, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RelayState"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ShNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["CommandTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, RelayState="0.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, CommandTimeUnixMs="1656869326637.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, RelayState=2) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ShNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, CommandTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + payload = GtDriverBooleanactuatorCmd.model_validate(d) + assert payload.model_dump() == d diff --git a/tests/types/test_gt_sh_booleanactuator_cmd_status.py b/tests/types/test_gt_sh_booleanactuator_cmd_status.py index 50a883fe..e4f98091 100644 --- a/tests/types/test_gt_sh_booleanactuator_cmd_status.py +++ b/tests/types/test_gt_sh_booleanactuator_cmd_status.py @@ -1,12 +1,6 @@ """Tests gt.sh.booleanactuator.cmd.status type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtShBooleanactuatorCmdStatus_Maker as Maker +from gwproto.types import GtShBooleanactuatorCmdStatus def test_gt_sh_booleanactuator_cmd_status_generated() -> None: @@ -17,74 +11,4 @@ def test_gt_sh_booleanactuator_cmd_status_generated() -> None: "TypeName": "gt.sh.booleanactuator.cmd.status", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - sh_node_alias=gtuple.ShNodeAlias, - relay_state_command_list=gtuple.RelayStateCommandList, - command_time_unix_ms_list=gtuple.CommandTimeUnixMsList, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ShNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["RelayStateCommandList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["CommandTimeUnixMsList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ShNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, CommandTimeUnixMsList=[1656245000]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShBooleanactuatorCmdStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_cli_atn_cmd.py b/tests/types/test_gt_sh_cli_atn_cmd.py index 47ac29b5..98971eaf 100644 --- a/tests/types/test_gt_sh_cli_atn_cmd.py +++ b/tests/types/test_gt_sh_cli_atn_cmd.py @@ -1,12 +1,6 @@ """Tests gt.sh.cli.atn.cmd type, version 110""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtShCliAtnCmd_Maker as Maker +from gwproto.types import GtShCliAtnCmd def test_gt_sh_cli_atn_cmd_generated() -> None: @@ -17,78 +11,4 @@ def test_gt_sh_cli_atn_cmd_generated() -> None: "TypeName": "gt.sh.cli.atn.cmd", "Version": "110", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - from_g_node_alias=gtuple.FromGNodeAlias, - send_snapshot=gtuple.SendSnapshot, - from_g_node_id=gtuple.FromGNodeId, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SendSnapshot"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, SendSnapshot="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, FromGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShCliAtnCmd.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_multipurpose_telemetry_status.py b/tests/types/test_gt_sh_multipurpose_telemetry_status.py index 1645b626..00a6b441 100644 --- a/tests/types/test_gt_sh_multipurpose_telemetry_status.py +++ b/tests/types/test_gt_sh_multipurpose_telemetry_status.py @@ -1,108 +1,16 @@ """Tests gt.sh.multipurpose.telemetry.status type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError -from gwproto.types import GtShMultipurposeTelemetryStatus_Maker as Maker +from gwproto.types import GtShMultipurposeTelemetryStatus def test_gt_sh_multipurpose_telemetry_status_generated() -> None: d = { "AboutNodeAlias": "a.elt1", "SensorNodeAlias": "a.m", - "TelemetryNameGtEnumSymbol": "af39eec9", + "TelemetryName": "PowerW", "ValueList": [4559], "ReadTimeUnixMsList": [1656443705023], "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - about_node_alias=gtuple.AboutNodeAlias, - sensor_node_alias=gtuple.SensorNodeAlias, - telemetry_name=gtuple.TelemetryName, - value_list=gtuple.ValueList, - read_time_unix_ms_list=gtuple.ReadTimeUnixMsList, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SensorNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ValueList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ReadTimeUnixMsList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, AboutNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ReadTimeUnixMsList=[1656245000]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShMultipurposeTelemetryStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_simple_telemetry_status.py b/tests/types/test_gt_sh_simple_telemetry_status.py index 3d802472..f2a637b8 100644 --- a/tests/types/test_gt_sh_simple_telemetry_status.py +++ b/tests/types/test_gt_sh_simple_telemetry_status.py @@ -1,101 +1,15 @@ """Tests gt.sh.simple.telemetry.status type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError -from gwproto.types import GtShSimpleTelemetryStatus_Maker as Maker +from gwproto.types import GtShSimpleTelemetryStatus def test_gt_sh_simple_telemetry_status_generated() -> None: d = { "ShNodeAlias": "a.elt1.relay", - "TelemetryNameGtEnumSymbol": "5a71d4b3", + "TelemetryName": "CurrentRmsMicroAmps", "ValueList": [0], "ReadTimeUnixMsList": [1656443705023], "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - sh_node_alias=gtuple.ShNodeAlias, - telemetry_name=gtuple.TelemetryName, - value_list=gtuple.ValueList, - read_time_unix_ms_list=gtuple.ReadTimeUnixMsList, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ShNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ValueList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ReadTimeUnixMsList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TelemetryNameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).TelemetryName == TelemetryName.default() - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ShNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ReadTimeUnixMsList=[1656245000]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShSimpleTelemetryStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index c4ba62f4..8f8b90da 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -1,12 +1,6 @@ """Tests gt.sh.status type, version 110""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtShStatus_Maker as Maker +from gwproto.types import GtShStatus def test_gt_sh_status_generated() -> None: @@ -23,7 +17,7 @@ def test_gt_sh_status_generated() -> None: "ShNodeAlias": "a.elt1.relay", "TypeName": "gt.sh.simple.telemetry.status", "Version": "100", - "TelemetryNameGtEnumSymbol": "5a71d4b3", + "TelemetryName": "RelayState", } ], "MultipurposeTelemetryList": [ @@ -34,7 +28,7 @@ def test_gt_sh_status_generated() -> None: "SensorNodeAlias": "a.m", "TypeName": "gt.sh.multipurpose.telemetry.status", "Version": "100", - "TelemetryNameGtEnumSymbol": "ad19e79c", + "TelemetryName": "CurrentRmsMicroAmps", } ], "BooleanactuatorCmdList": [ @@ -50,166 +44,4 @@ def test_gt_sh_status_generated() -> None: "TypeName": "gt.sh.status", "Version": "110", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - from_g_node_alias=gtuple.FromGNodeAlias, - from_g_node_id=gtuple.FromGNodeId, - about_g_node_alias=gtuple.AboutGNodeAlias, - slot_start_unix_s=gtuple.SlotStartUnixS, - reporting_period_s=gtuple.ReportingPeriodS, - simple_telemetry_list=gtuple.SimpleTelemetryList, - multipurpose_telemetry_list=gtuple.MultipurposeTelemetryList, - booleanactuator_cmd_list=gtuple.BooleanactuatorCmdList, - status_uid=gtuple.StatusUid, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SlotStartUnixS"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ReportingPeriodS"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SimpleTelemetryList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MultipurposeTelemetryList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["BooleanactuatorCmdList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["StatusUid"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, SlotStartUnixS="1656945300.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, ReportingPeriodS="300.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SimpleTelemetryList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SimpleTelemetryList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SimpleTelemetryList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, MultipurposeTelemetryList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, MultipurposeTelemetryList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, MultipurposeTelemetryList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, BooleanactuatorCmdList="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, BooleanactuatorCmdList=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, BooleanactuatorCmdList=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, FromGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, AboutGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SlotStartUnixS=32503683600) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, StatusUid="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py index 8419ee69..a60f819a 100644 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py @@ -1,101 +1,15 @@ """Tests gt.sh.telemetry.from.multipurpose.sensor type, version 100""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import GtShTelemetryFromMultipurposeSensor_Maker as Maker +from gwproto.types import GtShTelemetryFromMultipurposeSensor def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: d = { "ScadaReadTimeUnixMs": 1656587343297, "AboutNodeAliasList": ["a.elt1"], - "TelemetryNameList": ["ad19e79c"], + "TelemetryNameList": ["PowerW"], "ValueList": [18000], "TypeName": "gt.sh.telemetry.from.multipurpose.sensor", "Version": "100", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - scada_read_time_unix_ms=gtuple.ScadaReadTimeUnixMs, - about_node_alias_list=gtuple.AboutNodeAliasList, - telemetry_name_list=gtuple.TelemetryNameList, - value_list=gtuple.ValueList, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ScadaReadTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeAliasList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ValueList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ScadaReadTimeUnixMs="1656587343297.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ScadaReadTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, AboutNodeAliasList=["a.b-h"]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtShTelemetryFromMultipurposeSensor.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_telemetry.py b/tests/types/test_gt_telemetry.py index 729793f1..38549d1e 100644 --- a/tests/types/test_gt_telemetry.py +++ b/tests/types/test_gt_telemetry.py @@ -1,109 +1,15 @@ """Tests gt.telemetry type, version 110""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.enums import TelemetryName -from gwproto.errors import SchemaError -from gwproto.types import GtTelemetry_Maker as Maker +from gwproto.types import GtTelemetry def test_gt_telemetry_generated() -> None: d = { "ScadaReadTimeUnixMs": 1656513094288, "Value": 63430, - "NameGtEnumSymbol": "c89d0ba1", + "Name": "WaterTempCTimes1000", "Exponent": 3, "TypeName": "gt.telemetry", "Version": "110", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - scada_read_time_unix_ms=gtuple.ScadaReadTimeUnixMs, - value=gtuple.Value, - name=gtuple.Name, - exponent=gtuple.Exponent, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ScadaReadTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Value"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["NameGtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Exponent"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ScadaReadTimeUnixMs="1656513094288.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Value="63430.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, NameGtEnumSymbol="unknown_symbol") - assert Maker.dict_to_tuple(d2).Name == TelemetryName.default() - - d2 = dict(d, Exponent="3.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ScadaReadTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert GtTelemetry.model_validate(d).model_dump() == d diff --git a/tests/types/test_heartbeat_b.py b/tests/types/test_heartbeat_b.py index 1d75a83c..b031e62f 100644 --- a/tests/types/test_heartbeat_b.py +++ b/tests/types/test_heartbeat_b.py @@ -1,12 +1,6 @@ """Tests heartbeat.b type, version 001""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import HeartbeatB_Maker as Maker +from gwproto.types import HeartbeatB def test_heartbeat_b_generated() -> None: @@ -21,126 +15,4 @@ def test_heartbeat_b_generated() -> None: "TypeName": "heartbeat.b", "Version": "001", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - from_g_node_alias=gtuple.FromGNodeAlias, - from_g_node_instance_id=gtuple.FromGNodeInstanceId, - my_hex=gtuple.MyHex, - your_last_hex=gtuple.YourLastHex, - last_received_time_unix_ms=gtuple.LastReceivedTimeUnixMs, - send_time_unix_ms=gtuple.SendTimeUnixMs, - starting_over=gtuple.StartingOver, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeInstanceId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["MyHex"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["YourLastHex"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["LastReceivedTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["SendTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["StartingOver"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, LastReceivedTimeUnixMs="1673635764282.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs="1673635765317.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, StartingOver="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, FromGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeInstanceId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, MyHex="g") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, YourLastHex="g") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, LastReceivedTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, SendTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert HeartbeatB.model_validate(d).model_dump() == d diff --git a/tests/types/test_power_watts.py b/tests/types/test_power_watts.py index 09e18d94..0044dab0 100644 --- a/tests/types/test_power_watts.py +++ b/tests/types/test_power_watts.py @@ -1,12 +1,6 @@ """Tests power.watts type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import PowerWatts_Maker as Maker +from gwproto.types import PowerWatts def test_power_watts_generated() -> None: @@ -15,52 +9,4 @@ def test_power_watts_generated() -> None: "TypeName": "power.watts", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - watts=gtuple.Watts, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Watts"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, Watts="4500.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) + assert PowerWatts.model_validate(d).model_dump() == d diff --git a/tests/types/test_snapshot_spaceheat.py b/tests/types/test_snapshot_spaceheat.py index 70600d1a..82c1f421 100644 --- a/tests/types/test_snapshot_spaceheat.py +++ b/tests/types/test_snapshot_spaceheat.py @@ -1,12 +1,6 @@ """Tests snapshot.spaceheat type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import SnapshotSpaceheat_Maker as Maker +from gwproto.types import SnapshotSpaceheat def test_snapshot_spaceheat_generated() -> None: @@ -14,7 +8,7 @@ def test_snapshot_spaceheat_generated() -> None: "FromGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", "FromGNodeInstanceId": "0384ef21-648b-4455-b917-58a1172d7fc1", "Snapshot": { - "TelemetryNameList": ["5a71d4b3"], + "TelemetryNameList": ["RelayState"], "AboutNodeAliasList": ["a.elt1.relay"], "ReportTimeUnixMs": 1656363448000, "ValueList": [1], @@ -24,74 +18,4 @@ def test_snapshot_spaceheat_generated() -> None: "TypeName": "snapshot.spaceheat", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - from_g_node_alias=gtuple.FromGNodeAlias, - from_g_node_instance_id=gtuple.FromGNodeInstanceId, - snapshot=gtuple.Snapshot, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["FromGNodeInstanceId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Snapshot"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, FromGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, FromGNodeInstanceId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert SnapshotSpaceheat.model_validate(d).model_dump() == d diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py index d373c077..4ce0855c 100644 --- a/tests/types/test_ta_data_channels.py +++ b/tests/types/test_ta_data_channels.py @@ -1,12 +1,6 @@ """Tests ta.data.channels type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import TaDataChannels_Maker as Maker +from gwproto.types import TaDataChannels def test_ta_data_channels_generated() -> None: @@ -20,7 +14,7 @@ def test_ta_data_channels_generated() -> None: "DisplayName": "BoostPower", "AboutName": "a.elt1", "CapturedByName": "a.m", - "TelemetryNameGtEnumSymbol": "af39eec9", + "TelemetryName": "PowerW", "TypeName": "data.channel", "Version": "000", } @@ -29,116 +23,4 @@ def test_ta_data_channels_generated() -> None: "TypeName": "ta.data.channels", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - terminal_asset_g_node_alias=gtuple.TerminalAssetGNodeAlias, - terminal_asset_g_node_id=gtuple.TerminalAssetGNodeId, - time_unix_s=gtuple.TimeUnixS, - author=gtuple.Author, - channels=gtuple.Channels, - identifier=gtuple.Identifier, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TerminalAssetGNodeAlias"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TerminalAssetGNodeId"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TimeUnixS"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Author"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Channels"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["Identifier"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, TimeUnixS="1704142951.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Channels="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Channels=["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Channels=[{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, TerminalAssetGNodeAlias="a.b-h") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TerminalAssetGNodeId="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, TimeUnixS=32503683600) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, Identifier="d4be12d5-33ba-4f1f-b9e5") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert TaDataChannels.model_validate(d).model_dump() == d diff --git a/tests/types/test_telemetry_snapshot_spaceheat.py b/tests/types/test_telemetry_snapshot_spaceheat.py index 9ecfd31d..ba36892f 100644 --- a/tests/types/test_telemetry_snapshot_spaceheat.py +++ b/tests/types/test_telemetry_snapshot_spaceheat.py @@ -1,12 +1,6 @@ """Tests telemetry.snapshot.spaceheat type, version 000""" -import json - -import pytest -from pydantic import ValidationError - -from gwproto.errors import SchemaError -from gwproto.types import TelemetrySnapshotSpaceheat_Maker as Maker +from gwproto.types import TelemetrySnapshotSpaceheat def test_telemetry_snapshot_spaceheat_generated() -> None: @@ -14,88 +8,8 @@ def test_telemetry_snapshot_spaceheat_generated() -> None: "ReportTimeUnixMs": 1656363448000, "AboutNodeAliasList": ["a.elt1.relay", "a.tank.temp0"], "ValueList": [1, 66086], - "TelemetryNameList": ["5a71d4b3", "c89d0ba1"], + "TelemetryNameList": ["RelayState", "WaterTempCTimes1000"], "TypeName": "telemetry.snapshot.spaceheat", "Version": "000", } - - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) - - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - report_time_unix_ms=gtuple.ReportTimeUnixMs, - about_node_alias_list=gtuple.AboutNodeAliasList, - value_list=gtuple.ValueList, - telemetry_name_list=gtuple.TelemetryNameList, - ).tuple - assert t == gtuple - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ReportTimeUnixMs"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["AboutNodeAliasList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["ValueList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d) - del d2["TelemetryNameList"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - ###################################### - # Behavior on incorrect types - ###################################### - - d2 = dict(d, ReportTimeUnixMs="1656363448000.1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - d2 = dict(d, ReportTimeUnixMs=1656245000) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, AboutNodeAliasList=["a.b-h"]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - # End of Test + assert TelemetrySnapshotSpaceheat.model_validate(d).model_dump() == d From 77c4beaec3a66c1978aa6def8cdeddfd74cd9a2b Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 13 Sep 2024 12:51:54 -0400 Subject: [PATCH 108/168] Loosen coverage requirements while we refactor --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9104f16f..f3e0f2df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ source = ["gwproto", "tests"] [tool.coverage.report] show_missing = false -fail_under = 80 +fail_under = 70 [tool.black] extend_exclude = "src/gwproto/gt/|src/gwproto/gs/|src/gwproto/enums/" From c69532f66ab9fedf6f0e493e2a249596c9bfa495 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 13 Sep 2024 12:56:04 -0400 Subject: [PATCH 109/168] Remove used cac data_classes for cacs --- src/gwproto/data_classes/cacs/__init__.py | 0 .../data_classes/cacs/electric_meter_cac.py | 37 ---------------- .../cacs/fibaro_smart_implant_cac.py | 4 -- src/gwproto/data_classes/cacs/hubitat_cac.py | 4 -- .../data_classes/cacs/hubitat_poller_cac.py | 4 -- .../cacs/hubitat_tank_module_cac.py | 4 -- .../cacs/multipurpose_sensor_cac.py | 41 ------------------ .../data_classes/cacs/pipe_flow_sensor_cac.py | 31 ------------- src/gwproto/data_classes/cacs/relay_cac.py | 34 --------------- .../data_classes/cacs/resistive_heater_cac.py | 34 --------------- .../data_classes/cacs/rest_poller_cac.py | 4 -- .../cacs/simple_temp_sensor_cac.py | 43 ------------------- .../data_classes/cacs/web_server_cac.py | 4 -- .../data_classes/component_attribute_class.py | 29 ------------- src/gwproto/data_classes/hardware_layout.py | 6 +-- 15 files changed, 3 insertions(+), 276 deletions(-) delete mode 100644 src/gwproto/data_classes/cacs/__init__.py delete mode 100644 src/gwproto/data_classes/cacs/electric_meter_cac.py delete mode 100644 src/gwproto/data_classes/cacs/fibaro_smart_implant_cac.py delete mode 100644 src/gwproto/data_classes/cacs/hubitat_cac.py delete mode 100644 src/gwproto/data_classes/cacs/hubitat_poller_cac.py delete mode 100644 src/gwproto/data_classes/cacs/hubitat_tank_module_cac.py delete mode 100644 src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py delete mode 100644 src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py delete mode 100644 src/gwproto/data_classes/cacs/relay_cac.py delete mode 100644 src/gwproto/data_classes/cacs/resistive_heater_cac.py delete mode 100644 src/gwproto/data_classes/cacs/rest_poller_cac.py delete mode 100644 src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py delete mode 100644 src/gwproto/data_classes/cacs/web_server_cac.py delete mode 100644 src/gwproto/data_classes/component_attribute_class.py diff --git a/src/gwproto/data_classes/cacs/__init__.py b/src/gwproto/data_classes/cacs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/gwproto/data_classes/cacs/electric_meter_cac.py b/src/gwproto/data_classes/cacs/electric_meter_cac.py deleted file mode 100644 index e95732b8..00000000 --- a/src/gwproto/data_classes/cacs/electric_meter_cac.py +++ /dev/null @@ -1,37 +0,0 @@ -"""ElectricMeterCac definition""" - -from typing import Dict, List, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import LocalCommInterface, MakeModel, TelemetryName - - -class ElectricMeterCac(ComponentAttributeClass): - by_id: Dict[str, "ElectricMeterCac"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - make_model: MakeModel, - interface: LocalCommInterface, - poll_period_ms: int, - default_baud: int, - display_name: Optional[str] = None, - telemetry_name_list: Optional[List[TelemetryName]] = None, - ) -> None: - if telemetry_name_list is None: - telemetry_name_list = [] - super(self.__class__, self).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - self.default_baud = default_baud - self.poll_period_ms = poll_period_ms - self.interface = interface - self.make_model = make_model - self.telemetry_name_list = telemetry_name_list - ElectricMeterCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/fibaro_smart_implant_cac.py b/src/gwproto/data_classes/cacs/fibaro_smart_implant_cac.py deleted file mode 100644 index 0f1d651b..00000000 --- a/src/gwproto/data_classes/cacs/fibaro_smart_implant_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class FibaroSmartImplantCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/cacs/hubitat_cac.py b/src/gwproto/data_classes/cacs/hubitat_cac.py deleted file mode 100644 index d3e541ed..00000000 --- a/src/gwproto/data_classes/cacs/hubitat_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class HubitatCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/cacs/hubitat_poller_cac.py b/src/gwproto/data_classes/cacs/hubitat_poller_cac.py deleted file mode 100644 index 280c229f..00000000 --- a/src/gwproto/data_classes/cacs/hubitat_poller_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class HubitatPollerCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/cacs/hubitat_tank_module_cac.py b/src/gwproto/data_classes/cacs/hubitat_tank_module_cac.py deleted file mode 100644 index 7a2f8980..00000000 --- a/src/gwproto/data_classes/cacs/hubitat_tank_module_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class HubitatTankModuleCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py b/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py deleted file mode 100644 index f8a38331..00000000 --- a/src/gwproto/data_classes/cacs/multipurpose_sensor_cac.py +++ /dev/null @@ -1,41 +0,0 @@ -"""MultipurposeSensorCac definition""" - -from typing import Dict, List, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel, TelemetryName, Unit - - -class MultipurposeSensorCac(ComponentAttributeClass): - by_id: Dict[str, "MultipurposeSensorCac"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - exponent: int, - poll_period_ms: int, - temp_unit: Unit, - make_model: MakeModel, - telemetry_name_list: List[TelemetryName], - max_thermistors: Optional[int], - display_name: Optional[str] = None, - comms_method: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - - self.exponent = exponent - self.comms_method = comms_method - self.poll_period_ms = poll_period_ms - self.max_thermistors = max_thermistors - self.telemetry_name_list = telemetry_name_list - self.temp_unit = temp_unit - self.make_model = make_model - - MultipurposeSensorCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py b/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py deleted file mode 100644 index a5bfa63c..00000000 --- a/src/gwproto/data_classes/cacs/pipe_flow_sensor_cac.py +++ /dev/null @@ -1,31 +0,0 @@ -"""PipeFlowSensorCac definition""" - -from typing import Dict, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel - - -class PipeFlowSensorCac(ComponentAttributeClass): - by_id: Dict[str, "PipeFlowSensorCac"] = {} # noqa: RUF012 - - def __init__( - self, - component_attribute_class_id: str, - make_model: MakeModel, - display_name: Optional[str] = None, - comms_method: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - - self.comms_method = comms_method - self.make_model = make_model - - PipeFlowSensorCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/relay_cac.py b/src/gwproto/data_classes/cacs/relay_cac.py deleted file mode 100644 index 390cd00c..00000000 --- a/src/gwproto/data_classes/cacs/relay_cac.py +++ /dev/null @@ -1,34 +0,0 @@ -"""RelayCac definition""" - -from typing import Dict, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel, TelemetryName - - -class RelayCac(ComponentAttributeClass): - by_id: Dict[str, "RelayCac"] = {} # noqa: RUF012 - - def __init__( - self, - component_attribute_class_id: str, - make_model: MakeModel, - typical_response_time_ms: Optional[int], - display_name: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - - self.typical_response_time_ms = typical_response_time_ms - self.make_model = make_model - RelayCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" - - @property - def telemetry_name(self) -> TelemetryName: - return TelemetryName.RelayState diff --git a/src/gwproto/data_classes/cacs/resistive_heater_cac.py b/src/gwproto/data_classes/cacs/resistive_heater_cac.py deleted file mode 100644 index 98173844..00000000 --- a/src/gwproto/data_classes/cacs/resistive_heater_cac.py +++ /dev/null @@ -1,34 +0,0 @@ -"""ElectricHeaterCac definition""" - -from typing import Dict, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel - - -class ResistiveHeaterCac(ComponentAttributeClass): - by_id: Dict[str, "ResistiveHeaterCac"] = {} # noqa: RUF012 - - def __init__( - self, - component_attribute_class_id: str, - make_model: MakeModel, - nameplate_max_power_w: int, - rated_voltage_v: int, - display_name: Optional[str] = None, - ) -> None: - super( - self.__class__, - self, - ).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - self.make_model = make_model - self.nameplate_max_power_w = nameplate_max_power_w - self.rated_voltage_v = rated_voltage_v - ResistiveHeaterCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/rest_poller_cac.py b/src/gwproto/data_classes/cacs/rest_poller_cac.py deleted file mode 100644 index 817deadc..00000000 --- a/src/gwproto/data_classes/cacs/rest_poller_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class RESTPollerCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py b/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py deleted file mode 100644 index a77fbf0a..00000000 --- a/src/gwproto/data_classes/cacs/simple_temp_sensor_cac.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SimpleTempSensorCac definition""" - -from typing import Dict, Optional - -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass -from gwproto.enums import MakeModel, TelemetryName, Unit - - -class SimpleTempSensorCac(ComponentAttributeClass): - by_id: Dict[str, "SimpleTempSensorCac"] = {} # noqa: RUF012 - - def __init__( # noqa: PLR0913, PLR0917, RUF100 - self, - component_attribute_class_id: str, - exponent: int, - typical_response_time_ms: int, - temp_unit: Unit, - make_model: MakeModel, - telemetry_name: TelemetryName, - display_name: Optional[str] = None, - comms_method: Optional[str] = None, - ) -> None: - super(self.__class__, self).__init__( - component_attribute_class_id=component_attribute_class_id, - display_name=display_name, - ) - - self.exponent = exponent - self.comms_method = comms_method - self.typical_response_time_ms = typical_response_time_ms - self.telemetry_name = telemetry_name - self.temp_unit = temp_unit - self.make_model = make_model - - SimpleTempSensorCac.by_id[self.component_attribute_class_id] = self - ComponentAttributeClass.by_id[self.component_attribute_class_id] = self - if self.temp_unit not in {Unit.Celcius, Unit.Fahrenheit, Unit.Unitless}: - raise ValueError( - "TempSensorCac units must be Fahrenheit, Celsius or Unitless" - ) - - def __repr__(self) -> str: - return f"{self.make_model.value} {self.display_name}" diff --git a/src/gwproto/data_classes/cacs/web_server_cac.py b/src/gwproto/data_classes/cacs/web_server_cac.py deleted file mode 100644 index 3b3cbb96..00000000 --- a/src/gwproto/data_classes/cacs/web_server_cac.py +++ /dev/null @@ -1,4 +0,0 @@ -from gwproto.data_classes.component_attribute_class import ComponentAttributeClass - - -class WebServerCac(ComponentAttributeClass): ... diff --git a/src/gwproto/data_classes/component_attribute_class.py b/src/gwproto/data_classes/component_attribute_class.py deleted file mode 100644 index f4624dae..00000000 --- a/src/gwproto/data_classes/component_attribute_class.py +++ /dev/null @@ -1,29 +0,0 @@ -"""ComponentAttributeClass""" - -from abc import ABC -from typing import Any, Dict, Optional - -from gwproto.data_classes.mixin import StreamlinedSerializerMixin - - -class ComponentAttributeClass(ABC, StreamlinedSerializerMixin): - by_id: Dict[str, "ComponentAttributeClass"] = {} # noqa: RUF012 - - base_props = ["component_attribute_class_id", "display_name"] # noqa: RUF012 - - def __new__(cls, component_attribute_class_id, *args: Any, **kwargs: Any): # noqa: ANN001, ANN204, ANN401, ARG003 - try: - return cls.by_id[component_attribute_class_id] - except KeyError: - instance = super().__new__(cls) - cls.by_id[component_attribute_class_id] = instance - return instance - - def __init__( - self, component_attribute_class_id: str, display_name: Optional[str] = None - ) -> None: - self.component_attribute_class_id = component_attribute_class_id - self.display_name = display_name - - def __repr__(self) -> str: - return self.display_name or self.component_attribute_class_id diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index d07ff0cd..cc98c3f0 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -15,7 +15,6 @@ from typing import Any, List, Optional, Type, TypeVar import gwproto.data_classes.components -from gwproto.data_classes.cacs.electric_meter_cac import ElectricMeterCac from gwproto.data_classes.components import Component from gwproto.data_classes.components.component import ComponentOnly from gwproto.data_classes.components.electric_meter_component import ( @@ -35,6 +34,7 @@ from gwproto.types import ( ComponentAttributeClassGt, ComponentGt, + ElectricMeterCacGt, ) T = TypeVar("T") @@ -456,8 +456,8 @@ def power_meter_component(self) -> ElectricMeterComponent: return typing.cast(ElectricMeterComponent, self.power_meter_node.component) @cached_property - def power_meter_cac(self) -> ElectricMeterCac: - if not isinstance(self.power_meter_component.cac, ElectricMeterCac): + def power_meter_cac(self) -> ElectricMeterCacGt: + if not isinstance(self.power_meter_component.cac, ElectricMeterCacGt): raise TypeError( f"ERROR. power_meter_component cac {self.power_meter_component.cac}" f" / {type(self.power_meter_component.cac)} is not an ElectricMeterCac" From 3c13f17297795a0193d5b38b6ccbe35aa3c2c55c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Fri, 13 Sep 2024 14:35:05 -0400 Subject: [PATCH 110/168] property_format cleanups --- src/gwproto/property_format.py | 310 +++----------------------------- src/gwproto/types/hubitat_gt.py | 5 +- src/gwproto/utils.py | 7 - 3 files changed, 26 insertions(+), 296 deletions(-) diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 94662ffc..1c40a899 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -1,6 +1,5 @@ # ruff: noqa: ANN401 - -import string +import re import struct import uuid from datetime import datetime, timezone @@ -9,6 +8,9 @@ import pydantic from pydantic import BeforeValidator, Field +UTC_2000_01_01_TIMESTAMP = datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() +UTC_3000_01_01_TIMESTAMP = datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() + def predicate_validator( field_name: str, @@ -62,20 +64,26 @@ def _validator(v: Any) -> Any: return pydantic.field_validator(field_name, **kwargs)(_validator) -def is_hex_char(v: str) -> bool: - """HexChar format: single-char string in '0123456789abcdefABCDEF' +MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") - Returns: - bool: True if HexChar, false else - """ - if not isinstance(v, str): - return False - if len(v) > 1: + +def has_mac_address_format(mac_str: str) -> bool: + return bool(MAC_REGEX.match(mac_str.lower())) + + +def is_short_integer(candidate: int) -> bool: + try: + struct.pack("h", candidate) + except: # noqa return False - return not v not in "0123456789abcdefABCDEF" + return True -def check_is_hex_char(v: str) -> None: +def is_bit(candidate: int) -> bool: + return not candidate not in {0, 1} + + +def check_is_hex_char(v: str) -> str: """Checks HexChar format HexChar format: single-char string in '0123456789abcdefABCDEF' @@ -95,85 +103,6 @@ def check_is_hex_char(v: str) -> None: return v -HexChar = Annotated[str, BeforeValidator(check_is_hex_char)] - - -def is_valid_asa_name(candidate: str) -> bool: - """a string no more than 32 chars - - Args: - candidate (str): candidate - - Returns: - bool: True if a string no more than 32 cars - """ - try: - candidate_len = len(candidate) - except: # noqa - return False - return not candidate_len > 32 - - -def check_is_valid_asa_name(candidate: str) -> None: - try: - candidate_len = len(candidate) - except Exception as e: - raise ValueError(f"Not ValidAsaName: {e} /n {candidate} ") from e - if candidate_len > 32: - raise ValueError( - f"Not ValidAsaName: AsaNames must be <= 32 /n {candidate} is {len(candidate)}" - ) - - -def is_64_bit_hex(candidate: str) -> bool: - if len(candidate) != 8: - return False - return all(c in string.hexdigits for c in candidate) - - -def check_is_64_bit_hex(candidate: str) -> None: - if len(candidate) != 8: - raise ValueError(f" {candidate} Must be length 8, not {len(candidate)}") - if not all(c in string.hexdigits for c in candidate): - raise ValueError("Must be hex digits") - - -def is_bit(candidate: int) -> bool: - return not candidate not in {0, 1} - - -def check_is_bit(candidate: int) -> None: - if candidate not in {0, 1}: - raise ValueError(f"{candidate} must be either 0 or 1") - - -def is_left_right_dot(candidate: str) -> bool: - """Lowercase AlphanumericStrings separated by dots (i.e. periods), with most - significant word to the left. I.e. `d1.ne` is the child of `d1`. - Checking the format cannot verify the significance of words. All - words must be alphanumeric. Most significant word must start with - an alphabet charecter - - Args: - candidate (str): candidate - - Returns: - bool: True if is_lrod_alias_format - """ - try: - x = candidate.split(".") - except: # noqa - return False - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - return False - for word in x: - if not word.isalnum(): - return False - return candidate.islower() - - def check_is_left_right_dot(candidate: str) -> str: """Lowercase AlphanumericStrings separated by dots (i.e. periods), with most significant word to the left. I.e. `d1.ne` is the child of `d1`. @@ -205,9 +134,6 @@ def check_is_left_right_dot(candidate: str) -> str: return candidate -LeftRightDotStr = Annotated[str, BeforeValidator(check_is_left_right_dot)] - - def str_is_valid_uuid4(v: str) -> str: v = str(v) try: @@ -219,204 +145,16 @@ def str_is_valid_uuid4(v: str) -> str: return str(u) -UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] - - -def is_lru_alias_format(candidate: str) -> bool: - """AlphanumericStrings separated by underscores, with most - significant word to the left. I.e. `d1.ne` is the child of `d1`. - Checking the format cannot verify the significance of words. All - words must be alphanumeric. Most significant word must start with - an alphabet charecter""" - try: - x = candidate.split("_") - except: # noqa - return False - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - return False - for word in x: - if not word.isalnum(): - return False - return candidate.islower() - - -def is_lrh_alias_format(candidate: str) -> bool: - """AlphanumericStrings separated by hyphens, with most - significant word to the left. I.e. `d1.ne` is the child of `d1`. - Checking the format cannot verify the significance of words. All - words must be alphanumeric. Most significant word must start with - an alphabet charecter""" - try: - x = candidate.split("-") - except: # noqa - return False - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - return False - for word in x: - if not word.isalnum(): - return False - return candidate.islower() - - -def is_positive_integer(candidate: int) -> bool: - if not isinstance(candidate, int): - return False # type: ignore[unreachable] - return not candidate <= 0 - - -def check_is_positive_integer(candidate: int) -> None: - if not isinstance(candidate, int): - raise ValueError("Must be an integer") # noqa: TRY004 - if candidate <= 0: - raise ValueError("Must be positive integer") - - -def is_reasonable_unix_time_ms(candidate: int) -> bool: - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > candidate: # type: ignore[attr-defined] - return False - return ( - int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) >= candidate - ) +HexChar = Annotated[str, BeforeValidator(check_is_hex_char)] +LeftRightDotStr = Annotated[str, BeforeValidator(check_is_left_right_dot)] -UTC_2000_01_01_TIMESTAMP = datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() -UTC_3000_01_01_TIMESTAMP = datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() +UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] UTCSeconds = Annotated[ int, Field(ge=UTC_2000_01_01_TIMESTAMP, le=UTC_3000_01_01_TIMESTAMP) ] + UTCMilliseconds = Annotated[ int, Field(ge=UTC_2000_01_01_TIMESTAMP * 1000, le=UTC_3000_01_01_TIMESTAMP * 1000) ] - - -def check_is_reasonable_unix_time_ms(candidate: int) -> None: - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) > candidate: - raise ValueError("ReasonableUnixTimeMs must be after 2000 AD") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) < candidate: - raise ValueError("ReasonableUnixTimeMs must be before 3000 AD") - - -def is_reasonable_unix_time_s(candidate: int) -> bool: - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > candidate: - return False - return int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()) >= candidate - - -def check_is_reasonable_unix_time_s(candidate: int) -> None: - if int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp()) > candidate: - raise ValueError("ReasonableUnixTimeS must be after 2000 AD") - if int(datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp()): - raise ValueError("ReasonableUnixTimeS must be before 3000 AD") - - -def is_unsigned_short(candidate: int) -> bool: - try: - struct.pack("H", candidate) - except: # noqa - return False - return True - - -def check_is_unsigned_short(candidate: int) -> None: - try: - struct.pack("H", candidate) - except: # noqa: E722 - raise ValueError("requires 0 <= number <= 65535") - - -def is_short_integer(candidate: int) -> bool: - try: - struct.pack("h", candidate) - except: # noqa - return False - return True - - -def check_is_short_integer(candidate: int) -> None: - try: - struct.pack("h", candidate) - except: # noqa: E722 - raise ValueError("short format requires (-32767 -1) <= number <= 32767") - - -def is_uuid_canonical_textual(candidate: str) -> bool: # noqa: PLR0911 - try: - x = candidate.split("-") - except AttributeError: - return False - if len(x) != 5: - return False - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - return False - if len(x[0]) != 8: - return False - if len(x[1]) != 4: - return False - if len(x[2]) != 4: - return False - if len(x[3]) != 4: - return False - return len(x[4]) == 12 - - -def check_is_uuid_canonical_textual(candidate: str) -> None: - try: - x = candidate.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError("Did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: # noqa: PERF203 - raise ValueError("Words are not all hex") - if len(x[0]) != 8: - raise ValueError("Word 0 not of length 8") - if len(x[1]) != 4: - raise ValueError("Word 1 not of length 4") - if len(x[2]) != 4: - raise ValueError("Word 2 not of length 4") - if len(x[3]) != 4: - raise ValueError("Word 3 not of length 4") - if len(x[4]) != 12: - raise ValueError("Word 4 not of length 12") - - -def check_world_alias_matches_universe(g_node_alias: str, universe: str) -> None: - """ - Raises: - ValueError: if g_node_alias is not LRD format or if first word does not match universe - """ - check_is_left_right_dot(g_node_alias) - world_alias = g_node_alias.split(".")[0] - if universe == "dev" and world_alias[0] != "d": - raise ValueError( - f"World alias for dev universe must start with d. Got {world_alias}" - ) - - -def is_world_instance_name_format(candidate: str) -> bool: - try: - words = candidate.split("__") - except: # noqa - return False - if len(words) != 2: - return False - try: - int(words[1]) - except: # noqa - return False - try: - root_g_node_alias_words = words[0].split(".") - except: # noqa - return False - return not len(root_g_node_alias_words) > 1 diff --git a/src/gwproto/types/hubitat_gt.py b/src/gwproto/types/hubitat_gt.py index 3fe99f6d..03210548 100644 --- a/src/gwproto/types/hubitat_gt.py +++ b/src/gwproto/types/hubitat_gt.py @@ -3,9 +3,8 @@ import yarl from pydantic import BaseModel, ConfigDict -from gwproto.property_format import predicate_validator +from gwproto.property_format import has_mac_address_format, predicate_validator from gwproto.types.rest_poller_gt import URLArgs, URLConfig -from gwproto.utils import has_mac_address_format class HubitatGt(BaseModel): @@ -66,7 +65,7 @@ def urls(self) -> dict[str, Optional[yarl.URL]]: def refresh_url_config(self, device_id: int) -> URLConfig: config = self.maker_api_url_config() - config.url_path_format += "/devices/{device_id}/refresh" + config.url_path_format += "/devices/{device_id}/refresh" # noqa: RUF027 config.url_path_args["device_id"] = device_id return config diff --git a/src/gwproto/utils.py b/src/gwproto/utils.py index eb2c18c2..d76cf377 100644 --- a/src/gwproto/utils.py +++ b/src/gwproto/utils.py @@ -13,10 +13,3 @@ def snake_to_camel(word: str) -> str: def rld_alias(alias: str) -> str: return ".".join(reversed(alias.split("."))) - - -MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") - - -def has_mac_address_format(mac_str: str) -> bool: - return bool(MAC_REGEX.match(mac_str.lower())) From 19103d0e8aee403eb63d8ea23cdd6bac19b92387 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 16 Sep 2024 14:25:39 -0400 Subject: [PATCH 111/168] Simplify MQTT codecs, now that we only no longer use _Maker classes --- src/gwproto/__init__.py | 26 +- src/gwproto/decoders.py | 590 ++++++++++++--------------- src/gwproto/default_decoders.py | 63 +-- tests/dummy_decoders/child/codec.py | 14 +- tests/dummy_decoders/parent/codec.py | 17 +- 5 files changed, 273 insertions(+), 437 deletions(-) diff --git a/src/gwproto/__init__.py b/src/gwproto/__init__.py index afcab9ec..7997711b 100644 --- a/src/gwproto/__init__.py +++ b/src/gwproto/__init__.py @@ -1,20 +1,9 @@ from gwproto.data_classes.hardware_layout import HardwareLayout from gwproto.data_classes.sh_node import ShNode from gwproto.decoders import ( - CallableDecoder, - Decoder, - DecoderItem, - Decoders, - MakerDecoder, - MakerExtractor, MessageDiscriminator, MQTTCodec, - OneDecoderExtractor, - PydanticDecoder, - PydanticTypeNameDecoder, - create_discriminator, - create_message_payload_discriminator, - get_pydantic_literal_type_name, + create_message_model, pydantic_named_types, ) from gwproto.default_decoders import ( @@ -29,31 +18,20 @@ __all__ = [ "CacDecoder", - "CallableDecoder", "ComponentDecoder", "DecodedMQTTTopic", - "Decoder", - "DecoderItem", - "Decoders", "HardwareLayout", "Header", "MQTTCodec", "MQTTTopic", - "MakerDecoder", - "MakerExtractor", "Message", "MessageDiscriminator", - "OneDecoderExtractor", - "PydanticDecoder", - "PydanticTypeNameDecoder", "SchemaError", "ShNode", "as_enum", - "create_discriminator", - "create_message_payload_discriminator", + "create_message_model", "default_cac_decoder", "default_component_decoder", - "get_pydantic_literal_type_name", "messages", # noqa: F822 "property_format", # noqa: F822 "pydantic_named_types", diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index d3005aae..cb98ec12 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -2,17 +2,15 @@ import abc import inspect -import json import re import sys import typing from abc import abstractmethod -from dataclasses import dataclass +from types import ModuleType from typing import ( Any, - Callable, + Generic, Literal, - NamedTuple, Optional, Sequence, Type, @@ -23,232 +21,24 @@ import pydantic from pydantic import BaseModel, Field, ValidationError, create_model +from pydantic_core import ErrorDetails -from gwproto.message import Header, Message +from gwproto.message import Message from gwproto.messages import AnyEvent from gwproto.topic import MQTTTopic - - -class Decoder(abc.ABC): - def decode_str(self, content: str | bytes, encoding: str = "utf-8") -> Any: - if isinstance(content, bytes): - content = content.decode(encoding) - return self.decode_obj(json.loads(content)) - - @abstractmethod - def decode_obj(self, o: Any) -> Any: ... - - -class CallableDecoder(Decoder): - _decode_obj: Callable[[Any], Any] - - def __init__(self, decode_obj: Callable[[Any], Any]) -> None: - if not callable(decode_obj): - raise TypeError( - f"ERROR. decode_obj {decode_obj}/{type(decode_obj)} attribute is not Callable" - ) - self._decode_obj = decode_obj - - def decode_obj(self, o: Any) -> Any: - return self._decode_obj(o) - - -class PydanticDecoder(CallableDecoder): - def __init__(self, model: Type["BaseModel"]) -> None: - super().__init__(model.model_validate) - - -class MakerDecoder(CallableDecoder): - DECODER_FUNCTION_NAME: str = "dict_to_tuple" - - def __init__(self, model: Any) -> None: - decoder_function = getattr(model, self.DECODER_FUNCTION_NAME, None) - if decoder_function is None: - raise ValueError( - f"ERROR. {model} has no function {self.DECODER_FUNCTION_NAME}" - ) - super().__init__(decoder_function) - +from gwproto.types import ComponentAttributeClassGt, ComponentGt MessageDiscriminator = TypeVar("MessageDiscriminator", bound=Message[Any]) - -class MessageDecoder(Decoder): - decoders: "Decoders" - message_payload_discriminator: Optional[MessageDiscriminator] - - def __init__( - self, - decoders: "Decoders", - message_payload_discriminator: Optional[Type[MessageDiscriminator]] = None, - ) -> None: - self.decoders = decoders - self.message_payload_discriminator = message_payload_discriminator - - @classmethod - def _try_message_as_event(cls, message_dict: dict, e: ValidationError) -> Message: - special_typename_handling_message: Optional[Message[Any]] = None - for error in e.errors(): - if error.get("type", "") == "union_tag_invalid": - ctx = error.get("ctx", {}) - if ctx.get("discriminator", "") == "'TypeName'" and ctx.get( - "tag", "" - ).startswith("gridworks.event"): - try: - message_dict["Payload"] = AnyEvent(**message_dict["Payload"]) - special_typename_handling_message = Message(**message_dict) - except Exception as e2: # noqa: BLE001 - raise e2 from e - if special_typename_handling_message is None: - raise e - return special_typename_handling_message - - def decode_obj(self, o: Any) -> Message[Any]: - message_dict: dict = dict(o) - message_dict["Header"] = Header.model_validate(message_dict.get("Header", {})) - message: Message[Any] - if message_dict["Header"].MessageType in self.decoders: - message_dict["Payload"] = self.decoders.decode_obj( - message_dict["Header"].MessageType, - message_dict.get("Payload", {}), - ) - message = Message(**message_dict) - else: - try: - message = self.message_payload_discriminator.model_validate( - message_dict - ) - except pydantic.ValidationError as e: - # This can result because we receive a TypeName we don't recognize, either because the sender is - # newer or older than our code. In some cases we can still meaningfully interpret the message, - # for example if we can recognize that it is an event messasge, as is done here. - message = self._try_message_as_event(message_dict, e) - return message - - -class DecoderItem(NamedTuple): - type_name: str - decoder: Decoder - - -DEFAULT_TYPE_NAME_FIELD: str = "TypeName" -DEFAULT_PAYLOAD_FIELD: str = "Payload" -DEFAULT_EXCLUDED_TYPE_NAMES: set[str] = {Message.type_name()} - - -@dataclass -class OneDecoderExtractor: - type_name_field: str = DEFAULT_TYPE_NAME_FIELD - decoder_function_name: str = "parse_obj" - - def get_type_name(self, obj: Any) -> str: - if not (type_name := getattr(obj, self.type_name_field, "")): # noqa: SIM102 - if (decoder_fields := getattr(obj, "model_fields", None)) is not None: # noqa: SIM102 - if ( - model_field := decoder_fields.get(self.type_name_field, None) - ) is not None: - type_name = getattr(model_field, "default", "") - return type_name - - def get_decoder(self, obj: Any) -> Optional[Decoder]: - decoder_function = getattr(obj, self.decoder_function_name, None) - if callable(decoder_function): - decoder = CallableDecoder(decoder_function) - else: - decoder = None - return decoder - - def extract(self, obj: Any) -> Optional[DecoderItem]: - item = None - if type_name := self.get_type_name(obj): # noqa: SIM102 - if (decoder := self.get_decoder(obj)) is not None: - item = DecoderItem(type_name, decoder) - return item - - -@dataclass -class MakerExtractor(OneDecoderExtractor): - type_name_field: str = "type_name" - decoder_function_name: str = MakerDecoder.DECODER_FUNCTION_NAME - - -class Decoders: - _decoders: dict[str, Decoder] - - def __init__(self, decoders: Optional[dict[str, Decoder]] = None) -> None: - self._decoders = {} - if decoders is not None: - self.add_decoders(decoders) - - def decoder(self, type_name: str) -> Decoder: - return self._decoders[type_name] - - def decode_str( - self, type_name: str, content: str | bytes, encoding: str = "utf-8" - ) -> Any: - return self.decoder(type_name).decode_str(content, encoding=encoding) - - def decode_obj(self, type_name: str, o: Any) -> Any: - return self.decoder(type_name).decode_obj(o) - - def add_decoder(self, type_name: str, decoder: Decoder) -> "Decoders": - self._validate(type_name, decoder) - self._decoders[type_name] = decoder - return self - - def add_decoders(self, decoders: dict[str, Decoder]) -> "Decoders": - for type_name, decoder in decoders.items(): - self._validate(type_name, decoder) - self._decoders.update(decoders) - return self - - def merge(self, other: "Decoders") -> "Decoders": - self.add_decoders(other._decoders) # noqa: SLF001 - return self - - def __contains__(self, type_name: str) -> bool: - return type_name in self._decoders - - def types(self) -> list[str]: - return list(self._decoders.keys()) - - def _validate(self, type_name: str, decoder: Decoder) -> None: - if type_name in self._decoders and self._decoders[type_name] is not decoder: - raise ValueError( - f"ERROR. decoder for [{type_name}] is already present as [{self._decoders[type_name]}]" - ) - - @classmethod - def from_objects( - cls, - objs: Optional[list[Any]] = None, - message_payload_discriminator: Optional[Type[MessageDiscriminator]] = None, - extractors: Optional[Sequence[OneDecoderExtractor]] = None, - ) -> "Decoders": - items = {} - if objs is not None: - if extractors is None: - extractors = [OneDecoderExtractor(), MakerExtractor()] - for obj in objs: - for extractor in extractors: - item = extractor.extract(obj) - if item is not None: - items[item.type_name] = item.decoder - break - d = Decoders(items) - d.add_decoder( - Message.type_name(), MessageDecoder(d, message_payload_discriminator) - ) - return d +TYPE_NAME_FIELD: str = "TypeName" class MQTTCodec(abc.ABC): ENCODING = "utf-8" - decoders: Decoders + message_model: Type[Message[Any]] - def __init__(self, decoders: Decoders) -> None: - self.decoders = Decoders().merge(decoders) - super().__init__() + def __init__(self, message_model: Type[Message[Any]]) -> None: + self.message_model = message_model def encode(self, content: bytes | BaseModel) -> bytes: if isinstance(content, bytes): @@ -259,94 +49,183 @@ def encode(self, content: bytes | BaseModel) -> bytes: encoded = encoded.encode(self.ENCODING) return encoded - def decode(self, topic: str, payload: bytes) -> Any: + @classmethod + def get_unrecognized_payload_error( + cls, e: ValidationError + ) -> Optional[ErrorDetails]: + for error in e.errors(): + if error.get("type", "") == "union_tag_invalid" and error.get("loc"): + ctx = error.get("ctx", {}) + if ctx.get("discriminator") == "'TypeName'": + return error + return None + + def handle_unrecognized_payload( # noqa + self, payload: bytes, e: ValidationError, details: ErrorDetails + ) -> Message[Any]: + if details.get("ctx", {}).get("tag", "").startswith("gridworks.event"): + try: + return Message[AnyEvent].model_validate_json(payload) + except ValidationError as e2: + raise e2 from e + raise e + + def decode(self, topic: str, payload: bytes) -> Message: + self.validate_topic(topic) + try: + message = self.message_model.model_validate_json(payload) + except pydantic.ValidationError as e: + # ValidationError can result because we receive a TypeName we don't + # recognize, either because the sender is newer or older than our + # code. In some cases we can still meaningfully interpret the + # message, for example if we can recognize that it is an event + # message, as is done here. + if error_details := self.get_unrecognized_payload_error(e): + return self.handle_unrecognized_payload(payload, e, error_details) + raise + return message + + def validate_topic(self, topic: str) -> None: decoded_topic = MQTTTopic.decode(topic) - if decoded_topic.envelope_type not in self.decoders: + if decoded_topic.envelope_type != self.message_model.type_name(): raise ValueError( - f"Type {decoded_topic.envelope_type} not recognized. Available decoders: {self.decoders.types()}" + f"Type {decoded_topic.envelope_type} not recognized. " + f"Available decoders: {self.message_model.type_name()}" ) self.validate_source_alias(decoded_topic.src) - return self.decoders.decode_str( - decoded_topic.envelope_type, payload, encoding=self.ENCODING - ) @abstractmethod def validate_source_alias(self, source_alias: str) -> None: ... + @classmethod + def _try_message_as_event( + cls, payload: bytes, original_exception: ValidationError + ) -> Message: + for error in original_exception.errors(): + if error.get("type", "") == "union_tag_invalid": + ctx = error.get("ctx", {}) + if ctx.get("discriminator", "") == "'TypeName'" and ctx.get( + "tag", "" + ).startswith("gridworks.event"): + try: + return Message[AnyEvent].model_validate_json(payload) + except Exception as e2: # noqa: BLE001 + raise e2 from original_exception + raise original_exception + -def get_pydantic_literal_type_name( - o: Any, type_name_field: str = DEFAULT_TYPE_NAME_FIELD -) -> str: +def get_model_type_name(cls: Any) -> str: + """Return cls.TypeName, if cls inherits from BaseModel, has a TypeName field, + and that field is a Literal, else returns an empty string. + """ if ( - hasattr(o, "model_fields") - and type_name_field in o.model_fields - and get_origin(o.model_fields[type_name_field].annotation) == Literal + issubclass(cls, BaseModel) + and TYPE_NAME_FIELD in cls.model_fields + and get_origin(cls.model_fields[TYPE_NAME_FIELD].annotation) == Literal ): - return str(o.model_fields[type_name_field].default) + return str(cls.model_fields[TYPE_NAME_FIELD].default) return "" -def pydantic_named_types( # noqa: C901 +def get_candidate_modules( module_names: str | Sequence[str], modules: Optional[Sequence[Any]] = None, - type_name_field: str = DEFAULT_TYPE_NAME_FIELD, - type_name_regex: Optional[re.Pattern] = None, - excluded_type_names: Optional[set[str]] = None, -) -> list[Any]: - if excluded_type_names is None: - excluded_type_names = DEFAULT_EXCLUDED_TYPE_NAMES +) -> Sequence[ModuleType]: if isinstance(module_names, str): module_names = [module_names] if module_names else [] if unimported := [ module_name for module_name in module_names if module_name not in sys.modules ]: raise ValueError(f"ERROR. modules {unimported} have not been imported.") - named_types = [] - accumulated_types: dict[str, Any] = {} if modules is None: modules = [] - for module in [sys.modules[module_name] for module_name in module_names] + list( - modules + return [sys.modules[module_name] for module_name in module_names] + list(modules) + + +EXCLUDED_TYPE_NAMES: set[str] = {Message.type_name()} + + +def get_candidate_payload_classes( + module: ModuleType, +) -> Sequence[typing.Tuple[str, Type[BaseModel]]]: + """From a given module, return list of (TypeName, class) tuples for each + object in the module that: + * Is a class + * Is or inherits from BaseModel + * Has a TypeName which is a Literal with a value. + * TypeName is not in EXCLUDED_TYPE_NAMES, which can happen if the class + is a refinement of Message (e.g. PingMessage). + """ + return [ + (str(module_class.model_fields[TYPE_NAME_FIELD].default), module_class) + for _, module_class in inspect.getmembers(module, inspect.isclass) + if ( + issubclass(module_class, BaseModel) + and TYPE_NAME_FIELD in module_class.model_fields + and get_origin(module_class.model_fields[TYPE_NAME_FIELD].annotation) + == Literal + and len(str(module_class.model_fields[TYPE_NAME_FIELD].default)) > 0 + and str(module_class.model_fields[TYPE_NAME_FIELD].default) + not in EXCLUDED_TYPE_NAMES + ) + ] + + +def include_candidate_class( + type_name: str, + candidate_class: Type[BaseModel], + accumulated_types: dict[str, BaseModel], + type_name_regex: Optional[re.Pattern], +) -> bool: + if ( + type_name not in EXCLUDED_TYPE_NAMES + and type_name in accumulated_types + and accumulated_types[type_name] is not candidate_class ): - module_classes = [ - entry[1] for entry in inspect.getmembers(module, inspect.isclass) - ] - for module_class in module_classes: - if type_name := get_pydantic_literal_type_name( - module_class, type_name_field=type_name_field + raise ValueError( + f"ERROR {TYPE_NAME_FIELD} ({type_name}) " + f"for {candidate_class} already seen for " + f"class {accumulated_types[type_name]}" + ) + return not (type_name_regex is not None and not type_name_regex.match(type_name)) + + +def pydantic_named_types( + module_names: str | Sequence[str], + modules: Optional[Sequence[Any]] = None, + type_name_regex: Optional[re.Pattern] = None, +) -> list[Any]: + """Find Pyantic BaseModels with Literal 'TypeName' fields.""" + named_types = [] + accumulated_types: dict[str, Any] = {} + for module in get_candidate_modules(module_names, modules): + for type_name, candidate_class in get_candidate_payload_classes(module): + if ( + type_name in accumulated_types + and accumulated_types[type_name] is not candidate_class ): - if type_name in excluded_type_names: - continue - if type_name in accumulated_types: - if accumulated_types[type_name] is module_class: - continue - raise ValueError( - f"ERROR {type_name_field} ({type_name}) " - f"for {module_class} already seen for " - f"class {accumulated_types[type_name]}" - ) - if type_name_regex is not None and not type_name_regex.match(type_name): - continue - accumulated_types[type_name] = module_class - named_types.append(module_class) + raise ValueError( + f"ERROR {TYPE_NAME_FIELD} ({type_name}) " + f"for {candidate_class} already seen for " + f"class {accumulated_types[type_name]}" + ) + if type_name_regex is None or type_name_regex.match(type_name): + accumulated_types[type_name] = candidate_class + named_types.append(candidate_class) return named_types -def create_message_payload_discriminator( # noqa: PLR0913, PLR0917, RUF100 +def create_message_model( model_name: str, module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, - type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, - excluded_type_names: Optional[set[str]] = None, ) -> Type[MessageDiscriminator]: used_types = pydantic_named_types( module_names=module_names, modules=modules, - type_name_field=type_name_field, type_name_regex=type_name_regex, - excluded_type_names=excluded_type_names, ) if explicit_types is not None: used_types.extend(explicit_types) @@ -355,86 +234,141 @@ def create_message_payload_discriminator( # noqa: PLR0913, PLR0917, RUF100 __base__=Message, Payload=( Union[tuple(used_types)], - Field(..., discriminator=type_name_field), + Field(..., discriminator=TYPE_NAME_FIELD), ), ) -def create_discriminator( # noqa: PLR0913, PLR0917, RUF100 - model_name: str, - module_names: str | Sequence[str] = "", - modules: Optional[Sequence[Any]] = None, - explicit_types: Optional[Sequence[Any]] = None, - type_name_field: str = DEFAULT_TYPE_NAME_FIELD, - type_name_regex: Optional[re.Pattern] = None, - payload_field_name: str = DEFAULT_PAYLOAD_FIELD, - excluded_type_names: Optional[set[str]] = None, -) -> BaseModel: - used_types = pydantic_named_types( - module_names=module_names, - modules=modules, - type_name_field=type_name_field, - type_name_regex=type_name_regex, - excluded_type_names=excluded_type_names, - ) - if explicit_types is not None: - used_types.extend(explicit_types) - if len(used_types) == 1: - payload_field_type = ( - used_types[0], - Field(...), +WrappedT = TypeVar("WrappedT") + + +class UnionWrapper(BaseModel, Generic[WrappedT]): + """A utility class suitable for decoding a from union of types. In order + to treat input as a union of types, Pydantic requires that our union be in + named field, which by convention we call "Wrapped". + """ + + Wrapped: WrappedT + + @classmethod + def create( + cls, + model_name: str, + *, + module_names: str | Sequence[str] = "", + modules: Optional[Sequence[Any]] = None, + explicit_types: Optional[Sequence[Any]] = None, + type_name_regex: Optional[re.Pattern] = None, + ) -> "UnionWrapper": + """Create pydantic model that is a union of all appropriate types found via + module_names, modules and explicit_types""" + used_types = pydantic_named_types( + module_names=module_names, + modules=modules, + type_name_regex=type_name_regex, ) - else: - payload_field_type = ( - Union[tuple(used_types)], - Field(..., discriminator=type_name_field), + if explicit_types is not None: + used_types.extend(explicit_types) + if len(used_types) == 1: + payload_field_type = ( + used_types[0], + Field(...), + ) + else: + payload_field_type = ( + Union[tuple(used_types)], + Field(..., discriminator=TYPE_NAME_FIELD), + ) + # Pydantic requires us to put our discriminated union in a named field. + # We use the name 'Wrapped'. + return create_model( + model_name, __base__=UnionWrapper, Wrapped=payload_field_type ) - return create_model(model_name, **{payload_field_name: payload_field_type}) -class PydanticTypeNameDecoder(Decoder): - loader: BaseModel - payload_field_name: str - contains: set[str] +class UnionDecoder: + """A Utility base class for decoding from a union of types.""" + + loader: UnionWrapper - def __init__( # noqa: PLR0913 + def __init__( self, model_name: str, *, module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, - type_name_field: str = DEFAULT_TYPE_NAME_FIELD, type_name_regex: Optional[re.Pattern] = None, - payload_field_name: str = "Payload", ) -> None: - self.payload_field_name = payload_field_name - self.loader = create_discriminator( + self.loader = UnionWrapper.create( model_name=model_name, module_names=module_names, modules=modules, explicit_types=explicit_types, - type_name_field=type_name_field, type_name_regex=type_name_regex, - payload_field_name=payload_field_name, - ) - payload_field_annotation = self.loader.model_fields[ - self.payload_field_name - ].annotation - if payload_field_annotation is None: - raise ValueError( - f"ERROR. Payload field <{payload_field_name}> has no annotation" - ) - self.contains = { - payload_type.model_fields["TypeName"].default - for payload_type in typing.get_args(payload_field_annotation) - } - - def __contains__(self, type_name: str) -> bool: - return type_name in self.contains - - def decode_obj(self, data: dict) -> BaseModel: - return getattr( - self.loader.model_validate({self.payload_field_name: data}), - self.payload_field_name, ) + + +class CacDecoder(UnionDecoder): + TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") + loader: UnionWrapper + + def __init__( + self, + model_name: str, + type_name_regex: Optional[re.Pattern] = TYPE_NAME_REGEX, + **kwargs: Any, + ) -> None: + super().__init__(model_name, type_name_regex=type_name_regex, **kwargs) + + def decode( + self, cac_dict: dict, *, allow_missing: bool = True + ) -> ComponentAttributeClassGt: + try: + decoded = self.loader.model_validate({"Wrapped": cac_dict}).Wrapped + if not isinstance(decoded, ComponentAttributeClassGt): + raise TypeError( + f"ERROR. CacDecoder decoded type {type(decoded)}, " + "not ComponentAttributeClassGt" + ) + except ValidationError as e: + if allow_missing and any( + error.get("type") == "union_tag_invalid" for error in e.errors() + ): + decoded = ComponentAttributeClassGt(**cac_dict) + else: + raise + return decoded + + +class ComponentDecoder(UnionDecoder): + TYPE_NAME_REGEX = re.compile(r".*\.?component\.gt") + + def __init__( + self, + model_name: str, + type_name_regex: Optional[re.Pattern] = TYPE_NAME_REGEX, + **kwargs: Any, + ) -> None: + super().__init__(model_name, type_name_regex=type_name_regex, **kwargs) + + def decode( + self, component_dict: dict, *, allow_missing: bool = True + ) -> ComponentGt: + try: + # Pydantic requires that our union of types (components here) be in + # a named field, which by convention we call "Wrapped". + decoded = self.loader.model_validate({"Wrapped": component_dict}).Wrapped + if not isinstance(decoded, ComponentGt): + raise TypeError( + f"ERROR. ComponentDecoder decoded type {type(decoded)}, " + "not ComponentGt" + ) + except ValidationError as e: + if allow_missing and any( + error.get("type") == "union_tag_invalid" for error in e.errors() + ): + decoded = ComponentGt(**component_dict) + else: + raise + return decoded diff --git a/src/gwproto/default_decoders.py b/src/gwproto/default_decoders.py index 8333c82a..c388b7c8 100644 --- a/src/gwproto/default_decoders.py +++ b/src/gwproto/default_decoders.py @@ -1,70 +1,9 @@ # ruff: noqa: ANN401, RUF100, -import re -from typing import Any, TypeVar -from pydantic import ValidationError import gwproto.types.cacs import gwproto.types.components -from gwproto.decoders import PydanticTypeNameDecoder -from gwproto.types import ComponentAttributeClassGt, ComponentGt - -T = TypeVar("T") - - -class CacDecoder(PydanticTypeNameDecoder): - TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") - - def __init__(self, model_name: str, **kwargs: Any) -> None: - if "type_name_regex" not in kwargs: - kwargs["type_name_regex"] = CacDecoder.TYPE_NAME_REGEX - super().__init__(model_name, **kwargs) - - def decode( - self, d: dict, *, allow_missing: bool = True - ) -> ComponentAttributeClassGt: - try: - decoded = self.loader.model_validate({self.payload_field_name: d}).Payload - if not isinstance(decoded, ComponentAttributeClassGt): - raise TypeError( - f"ERROR. CacDecoder decoded type {type(decoded)}, " - "not ComponentAttributeClassGt" - ) - except ValidationError as e: - if allow_missing and any( - error.get("type") == "union_tag_invalid" for error in e.errors() - ): - decoded = ComponentAttributeClassGt(**d) - else: - raise - return decoded - - -class ComponentDecoder(PydanticTypeNameDecoder): - TYPE_NAME_REGEX = re.compile(r".*\.?component\.gt") - - def __init__(self, model_name: str, **kwargs: Any) -> None: - if "type_name_regex" not in kwargs: - kwargs["type_name_regex"] = ComponentDecoder.TYPE_NAME_REGEX - super().__init__(model_name, **kwargs) - - def decode(self, d: dict, *, allow_missing: bool = True) -> ComponentGt: - try: - decoded = self.loader.model_validate({self.payload_field_name: d}).Payload - if not isinstance(decoded, ComponentGt): - raise TypeError( - f"ERROR. ComponentDecoder decoded type {type(decoded)}, " - "not ComponentGt" - ) - except ValidationError as e: - if allow_missing and any( - error.get("type") == "union_tag_invalid" for error in e.errors() - ): - decoded = ComponentGt(**d) - else: - raise - return decoded - +from gwproto.decoders import CacDecoder, ComponentDecoder default_cac_decoder = CacDecoder( model_name="DefaultCacDecoder", diff --git a/tests/dummy_decoders/child/codec.py b/tests/dummy_decoders/child/codec.py index 9700aab7..370ec6d0 100644 --- a/tests/dummy_decoders/child/codec.py +++ b/tests/dummy_decoders/child/codec.py @@ -1,19 +1,13 @@ -from gwproto import Decoders, MQTTCodec, create_message_payload_discriminator +from gwproto import MQTTCodec, create_message_model from tests.dummy_decoders import PARENT -ChildMessageDecoder = create_message_payload_discriminator( - "ChildMessageDecoder", - [ - "gwproto.messages", - ], -) - class ChildMQTTCodec(MQTTCodec): def __init__(self) -> None: super().__init__( - Decoders.from_objects( - message_payload_discriminator=ChildMessageDecoder, + create_message_model( + "ChildMessageDecoder", + ["gwproto.messages"], ) ) diff --git a/tests/dummy_decoders/parent/codec.py b/tests/dummy_decoders/parent/codec.py index e2d7513e..b8ca653c 100644 --- a/tests/dummy_decoders/parent/codec.py +++ b/tests/dummy_decoders/parent/codec.py @@ -1,25 +1,16 @@ from gwproto import ( - CallableDecoder, - Decoders, MQTTCodec, - create_message_payload_discriminator, + create_message_model, ) -from gwproto.gs import GsPwr_Maker from tests.dummy_decoders import CHILD -ParentMessageDecoder = create_message_payload_discriminator( - model_name="ParentMessageDecoder", - module_names=["gwproto.messages"], -) - class ParentMQTTCodec(MQTTCodec): def __init__(self) -> None: super().__init__( - Decoders.from_objects( - message_payload_discriminator=ParentMessageDecoder, - ).add_decoder( - "p", CallableDecoder(lambda decoded: GsPwr_Maker(decoded[0]).tuple) + create_message_model( + model_name="ParentMessageDecoder", + module_names=["gwproto.messages"], ) ) From 3bbed07012a87046f4bb8226b4422f86342d9b98 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 16 Sep 2024 14:40:29 -0400 Subject: [PATCH 112/168] Revert RelayState to be an int constrained to 0 and 1, not a bool --- src/gwproto/property_format.py | 16 ++++++++++++---- src/gwproto/types/gt_dispatch_boolean.py | 4 ++-- src/gwproto/types/gt_dispatch_boolean_local.py | 4 ++-- .../types/gt_driver_booleanactuator_cmd.py | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index 1c40a899..ff8fe5eb 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -79,10 +79,6 @@ def is_short_integer(candidate: int) -> bool: return True -def is_bit(candidate: int) -> bool: - return not candidate not in {0, 1} - - def check_is_hex_char(v: str) -> str: """Checks HexChar format @@ -145,6 +141,16 @@ def str_is_valid_uuid4(v: str) -> str: return str(u) +def is_bit(candidate: int) -> bool: + return not candidate not in (0, 1) + + +def check_is_bit(candidate: int) -> int: + if candidate not in (0, 1): + raise ValueError(f"Candidate must be 0 or 1, Got {candidate}") + return candidate + + HexChar = Annotated[str, BeforeValidator(check_is_hex_char)] LeftRightDotStr = Annotated[str, BeforeValidator(check_is_left_right_dot)] @@ -158,3 +164,5 @@ def str_is_valid_uuid4(v: str) -> str: UTCMilliseconds = Annotated[ int, Field(ge=UTC_2000_01_01_TIMESTAMP * 1000, le=UTC_3000_01_01_TIMESTAMP * 1000) ] + +Bit = Annotated[int, BeforeValidator(check_is_bit)] diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py index 3d91f388..f3f614a2 100644 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ b/src/gwproto/types/gt_dispatch_boolean.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field -from gwproto.property_format import LeftRightDotStr, UTCMilliseconds, UUID4Str +from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds, UUID4Str class GtDispatchBoolean(BaseModel): @@ -18,7 +18,7 @@ class GtDispatchBoolean(BaseModel): ToGNodeAlias: LeftRightDotStr FromGNodeAlias: LeftRightDotStr FromGNodeInstanceId: UUID4Str - RelayState: bool = Field( + RelayState: Bit = Field( title="Relay State (False or True)", description=( "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py index 129b6401..b9713549 100644 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ b/src/gwproto/types/gt_dispatch_boolean_local.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field -from gwproto.property_format import LeftRightDotStr, UTCMilliseconds +from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds class GtDispatchBooleanLocal(BaseModel): @@ -17,7 +17,7 @@ class GtDispatchBooleanLocal(BaseModel): AboutNodeName: LeftRightDotStr FromNodeName: LeftRightDotStr - RelayState: bool = Field( + RelayState: Bit = Field( title="Relay State (False or True)", description=( "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py index c37a8db0..9ec7559a 100644 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ b/src/gwproto/types/gt_driver_booleanactuator_cmd.py @@ -4,7 +4,7 @@ from pydantic import BaseModel -from gwproto.property_format import LeftRightDotStr, UTCMilliseconds +from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds class GtDriverBooleanactuatorCmd(BaseModel): @@ -17,7 +17,7 @@ class GtDriverBooleanactuatorCmd(BaseModel): [More info](https://gridworks.readthedocs.io/en/latest/relay-state.html) """ - RelayState: bool + RelayState: Bit ShNodeAlias: LeftRightDotStr CommandTimeUnixMs: UTCMilliseconds TypeName: Literal["gt.driver.booleanactuator.cmd"] = "gt.driver.booleanactuator.cmd" From ac926045ff65d8b83f26fde6f104fb8e893619c7 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Mon, 16 Sep 2024 15:30:18 -0400 Subject: [PATCH 113/168] decoders: add missing check on where a union match failed --- src/gwproto/decoders.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index cb98ec12..a500b8e5 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -54,7 +54,9 @@ def get_unrecognized_payload_error( cls, e: ValidationError ) -> Optional[ErrorDetails]: for error in e.errors(): - if error.get("type", "") == "union_tag_invalid" and error.get("loc"): + if error.get("type", "") == "union_tag_invalid" and error.get("loc") == ( + "Payload", + ): ctx = error.get("ctx", {}) if ctx.get("discriminator") == "'TypeName'": return error From d925d5681db675c7d06bdf37206823d824ac769c Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Sat, 21 Sep 2024 14:17:42 -0400 Subject: [PATCH 114/168] Mypy: Small progress on a few files (#351) Mypy: progress on a few files --- .pre-commit-config.yaml | 2 +- noxfile.py | 2 + poetry.lock | 478 +++++++++++++++------- pyproject.toml | 5 +- src/gwproto/decoders.py | 44 +- src/gwproto/message.py | 29 +- src/gwproto/property_format.py | 63 +-- src/gwproto/types/hubitat_component_gt.py | 3 +- src/gwproto/types/hubitat_gt.py | 47 ++- src/gwproto/types/rest_poller_gt.py | 46 ++- tests/types/test_hubitat_gt.py | 5 +- 11 files changed, 446 insertions(+), 278 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2297e80e..116d9b03 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: args: [--py37-plus] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.3 + rev: v0.6.6 hooks: - id: ruff - id: ruff-format diff --git a/noxfile.py b/noxfile.py index abb12d22..f11a5119 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,3 +1,5 @@ +# type: ignore # noqa: PGH003 + """Nox sessions.""" import os diff --git a/poetry.lock b/poetry.lock index d9d52c98..f351e97e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -24,13 +24,13 @@ files = [ [[package]] name = "anyio" -version = "4.4.0" +version = "4.5.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.5.0-py3-none-any.whl", hash = "sha256:fdeb095b7cc5a5563175eedd926ec4ae55413bb4be5770c424af0ba46ccb4a78"}, + {file = "anyio-4.5.0.tar.gz", hash = "sha256:c5a275fe5ca0afd788001f58fca1e69e29ce706d746e317d660e21f70c530ef9"}, ] [package.dependencies] @@ -38,9 +38,9 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "babel" @@ -331,18 +331,18 @@ files = [ [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -391,13 +391,13 @@ files = [ [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -405,15 +405,18 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.8" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "imagesize" version = "1.4.1" @@ -453,6 +456,160 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "lxml" +version = "5.3.0" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"}, + {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"}, + {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"}, + {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"}, + {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"}, + {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"}, + {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"}, + {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"}, + {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"}, + {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"}, + {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"}, + {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"}, + {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"}, + {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"}, + {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"}, + {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"}, + {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"}, + {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"}, + {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"}, + {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"}, + {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"}, + {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.11)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -725,6 +882,7 @@ files = [ ] [package.dependencies] +lxml = {version = "*", optional = true, markers = "extra == \"reports\""} mypy-extensions = ">=1.0.0" typing-extensions = ">=4.6.0" @@ -809,13 +967,13 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -883,18 +1041,18 @@ files = [ [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.3" +pydantic-core = "2.23.4" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, @@ -906,105 +1064,127 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, - {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, - {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydantic-extra-types" +version = "2.9.0" +description = "Extra Pydantic types." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_extra_types-2.9.0-py3-none-any.whl", hash = "sha256:f0bb975508572ba7bf3390b7337807588463b7248587e69f43b1ad7c797530d0"}, + {file = "pydantic_extra_types-2.9.0.tar.gz", hash = "sha256:e061c01636188743bb69f368dcd391f327b8cfbfede2fe1cbb1211b06601ba3b"}, +] + +[package.dependencies] +pydantic = ">=2.5.2" + +[package.extras] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)", "pytz (>=2024.1)", "semver (>=3.0.2)", "tzdata (>=2024.1)"] +pendulum = ["pendulum (>=3.0.0,<4.0.0)"] +phonenumbers = ["phonenumbers (>=8,<9)"] +pycountry = ["pycountry (>=23)"] +python-ulid = ["python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] +semver = ["semver (>=3.0.2)"] + [[package]] name = "pyflakes" version = "3.2.0" @@ -1052,13 +1232,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -1255,29 +1435,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.4" +version = "0.6.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.4-py3-none-linux_armv6l.whl", hash = "sha256:c4b153fc152af51855458e79e835fb6b933032921756cec9af7d0ba2aa01a258"}, - {file = "ruff-0.6.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bedff9e4f004dad5f7f76a9d39c4ca98af526c9b1695068198b3bda8c085ef60"}, - {file = "ruff-0.6.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d02a4127a86de23002e694d7ff19f905c51e338c72d8e09b56bfb60e1681724f"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7862f42fc1a4aca1ea3ffe8a11f67819d183a5693b228f0bb3a531f5e40336fc"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eebe4ff1967c838a1a9618a5a59a3b0a00406f8d7eefee97c70411fefc353617"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:932063a03bac394866683e15710c25b8690ccdca1cf192b9a98260332ca93408"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:50e30b437cebef547bd5c3edf9ce81343e5dd7c737cb36ccb4fe83573f3d392e"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44536df7b93a587de690e124b89bd47306fddd59398a0fb12afd6133c7b3818"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ea086601b22dc5e7693a78f3fcfc460cceabfdf3bdc36dc898792aba48fbad6"}, - {file = "ruff-0.6.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b52387d3289ccd227b62102c24714ed75fbba0b16ecc69a923a37e3b5e0aaaa"}, - {file = "ruff-0.6.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0308610470fcc82969082fc83c76c0d362f562e2f0cdab0586516f03a4e06ec6"}, - {file = "ruff-0.6.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:803b96dea21795a6c9d5bfa9e96127cc9c31a1987802ca68f35e5c95aed3fc0d"}, - {file = "ruff-0.6.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:66dbfea86b663baab8fcae56c59f190caba9398df1488164e2df53e216248baa"}, - {file = "ruff-0.6.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:34d5efad480193c046c86608dbba2bccdc1c5fd11950fb271f8086e0c763a5d1"}, - {file = "ruff-0.6.4-py3-none-win32.whl", hash = "sha256:f0f8968feea5ce3777c0d8365653d5e91c40c31a81d95824ba61d871a11b8523"}, - {file = "ruff-0.6.4-py3-none-win_amd64.whl", hash = "sha256:549daccee5227282289390b0222d0fbee0275d1db6d514550d65420053021a58"}, - {file = "ruff-0.6.4-py3-none-win_arm64.whl", hash = "sha256:ac4b75e898ed189b3708c9ab3fc70b79a433219e1e87193b4f2b77251d058d14"}, - {file = "ruff-0.6.4.tar.gz", hash = "sha256:ac3b5bfbee99973f80aa1b7cbd1c9cbce200883bdd067300c22a6cc1c7fba212"}, + {file = "ruff-0.6.6-py3-none-linux_armv6l.whl", hash = "sha256:f5bc5398457484fc0374425b43b030e4668ed4d2da8ee7fdda0e926c9f11ccfb"}, + {file = "ruff-0.6.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:515a698254c9c47bb84335281a170213b3ee5eb47feebe903e1be10087a167ce"}, + {file = "ruff-0.6.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6bb1b4995775f1837ab70f26698dd73852bbb82e8f70b175d2713c0354fe9182"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c546f412dfae8bb9cc4f27f0e45cdd554e42fecbb34f03312b93368e1cd0a6"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59627e97364329e4eae7d86fa7980c10e2b129e2293d25c478ebcb861b3e3fd6"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94c3f78c3d32190aafbb6bc5410c96cfed0a88aadb49c3f852bbc2aa9783a7d8"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:704da526c1e137f38c8a067a4a975fe6834b9f8ba7dbc5fd7503d58148851b8f"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efeede5815a24104579a0f6320660536c5ffc1c91ae94f8c65659af915fb9de9"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e368aef0cc02ca3593eae2fb8186b81c9c2b3f39acaaa1108eb6b4d04617e61f"}, + {file = "ruff-0.6.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2653fc3b2a9315bd809725c88dd2446550099728d077a04191febb5ea79a4f79"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bb858cd9ce2d062503337c5b9784d7b583bcf9d1a43c4df6ccb5eab774fbafcb"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:488f8e15c01ea9afb8c0ba35d55bd951f484d0c1b7c5fd746ce3c47ccdedce68"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:aefb0bd15f1cfa4c9c227b6120573bb3d6c4ee3b29fb54a5ad58f03859bc43c6"}, + {file = "ruff-0.6.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a4c0698cc780bcb2c61496cbd56b6a3ac0ad858c966652f7dbf4ceb029252fbe"}, + {file = "ruff-0.6.6-py3-none-win32.whl", hash = "sha256:aadf81ddc8ab5b62da7aae78a91ec933cbae9f8f1663ec0325dae2c364e4ad84"}, + {file = "ruff-0.6.6-py3-none-win_amd64.whl", hash = "sha256:0adb801771bc1f1b8cf4e0a6fdc30776e7c1894810ff3b344e50da82ef50eeb1"}, + {file = "ruff-0.6.6-py3-none-win_arm64.whl", hash = "sha256:4b4d32c137bc781c298964dd4e52f07d6f7d57c03eae97a72d97856844aa510a"}, + {file = "ruff-0.6.6.tar.gz", hash = "sha256:0fc030b6fd14814d69ac0196396f6761921bd20831725c7361e1b8100b818034"}, ] [[package]] @@ -1349,13 +1529,13 @@ test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools [[package]] name = "sphinx-autobuild" -version = "2024.9.3" +version = "2024.9.19" description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser." optional = false python-versions = ">=3.9" files = [ - {file = "sphinx_autobuild-2024.9.3-py3-none-any.whl", hash = "sha256:55fe9bcc05dab659650d79bed0e6beb8b6032234edbf23f028f2cac3471f0c2d"}, - {file = "sphinx_autobuild-2024.9.3.tar.gz", hash = "sha256:75929a5a92b932da8d29837406d6d973a927c456f30986a27f1f20b067897892"}, + {file = "sphinx_autobuild-2024.9.19-py3-none-any.whl", hash = "sha256:57d974eebfc6461ff0fd136e78bf7a9c057d543d5166d318a45599898019b82c"}, + {file = "sphinx_autobuild-2024.9.19.tar.gz", hash = "sha256:2dd4863d174e533c1cd075eb5dfc90ad9a21734af7efd25569bf228b405e08ef"}, ] [package.dependencies] @@ -1544,13 +1724,13 @@ test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] [[package]] name = "types-pytz" -version = "2024.1.0.20240417" +version = "2024.2.0.20240913" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"}, - {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"}, + {file = "types-pytz-2024.2.0.20240913.tar.gz", hash = "sha256:4433b5df4a6fc587bbed41716d86a5ba5d832b4378e506f40d34bc9c81df2c24"}, + {file = "types_pytz-2024.2.0.20240913-py3-none-any.whl", hash = "sha256:a1eebf57ebc6e127a99d2fa2ba0a88d2b173784ef9b3defcc2004ab6855a44df"}, ] [[package]] @@ -1566,13 +1746,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -1601,13 +1781,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.26.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, ] [package.dependencies] @@ -1948,4 +2128,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e7c21e590eb34a76e99b81d61a6e7180cc49bbc1b3544a3d578764caa1c297cc" +content-hash = "f2c126344fd53b7ab7687b7cd6cd7923b7da283f71404bc5bf2a5d6065a4ee76" diff --git a/pyproject.toml b/pyproject.toml index f3e0f2df..2d44444b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,12 +23,13 @@ python = "^3.11" pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" +pydantic-extra-types = "^2.9.0" [tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" coverage = {extras = ["toml"], version = ">=6.2"} furo = ">=2021.11.12" -mypy = ">=0.930" +mypy = {extras = ["reports"], version = "^1.11.2"} pep8-naming = ">=0.12.1" pre-commit = ">=2.16.0" pre-commit-hooks = ">=4.1.0" @@ -42,7 +43,7 @@ xdoctest = {extras = ["colors"], version = ">=0.15.10"} myst-parser = {version = ">=0.16.1"} types-pytz = ">=2022.4.0.0" rich = ">=12.6.0" -ruff = "^0.6.3" +ruff = "^0.6.6" [tool.coverage.paths] source = ["src", "*/site-packages"] diff --git a/src/gwproto/decoders.py b/src/gwproto/decoders.py index a500b8e5..c67b603e 100644 --- a/src/gwproto/decoders.py +++ b/src/gwproto/decoders.py @@ -6,7 +6,9 @@ import sys import typing from abc import abstractmethod -from types import ModuleType + +# Static analysis (mypy, pycharm) thinks 'types' here is gwproto.types. +from types import ModuleType # noqa from typing import ( Any, Generic, @@ -40,13 +42,11 @@ class MQTTCodec(abc.ABC): def __init__(self, message_model: Type[Message[Any]]) -> None: self.message_model = message_model - def encode(self, content: bytes | BaseModel) -> bytes: + def encode(self, content: bytes | BaseModel) -> bytes: # noqa if isinstance(content, bytes): encoded = content else: - encoded = content.model_dump_json() - if not isinstance(encoded, bytes): - encoded = encoded.encode(self.ENCODING) + encoded = content.model_dump_json().encode() return encoded @classmethod @@ -72,7 +72,7 @@ def handle_unrecognized_payload( # noqa raise e2 from e raise e - def decode(self, topic: str, payload: bytes) -> Message: + def decode(self, topic: str, payload: bytes) -> Message[Any]: self.validate_topic(topic) try: message = self.message_model.model_validate_json(payload) @@ -102,7 +102,7 @@ def validate_source_alias(self, source_alias: str) -> None: ... @classmethod def _try_message_as_event( cls, payload: bytes, original_exception: ValidationError - ) -> Message: + ) -> Message[Any]: for error in original_exception.errors(): if error.get("type", "") == "union_tag_invalid": ctx = error.get("ctx", {}) @@ -176,8 +176,8 @@ def get_candidate_payload_classes( def include_candidate_class( type_name: str, candidate_class: Type[BaseModel], - accumulated_types: dict[str, BaseModel], - type_name_regex: Optional[re.Pattern], + accumulated_types: dict[str, Type[BaseModel]], + type_name_regex: Optional[re.Pattern[str]], ) -> bool: if ( type_name not in EXCLUDED_TYPE_NAMES @@ -195,7 +195,7 @@ def include_candidate_class( def pydantic_named_types( module_names: str | Sequence[str], modules: Optional[Sequence[Any]] = None, - type_name_regex: Optional[re.Pattern] = None, + type_name_regex: Optional[re.Pattern[str]] = None, ) -> list[Any]: """Find Pyantic BaseModels with Literal 'TypeName' fields.""" named_types = [] @@ -222,8 +222,8 @@ def create_message_model( module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, - type_name_regex: Optional[re.Pattern] = None, -) -> Type[MessageDiscriminator]: + type_name_regex: Optional[re.Pattern[str]] = None, +) -> Type[Message[Any]]: used_types = pydantic_named_types( module_names=module_names, modules=modules, @@ -260,8 +260,8 @@ def create( module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, - type_name_regex: Optional[re.Pattern] = None, - ) -> "UnionWrapper": + type_name_regex: Optional[re.Pattern[str]] = None, + ) -> Type["UnionWrapper[WrappedT]"]: """Create pydantic model that is a union of all appropriate types found via module_names, modules and explicit_types""" used_types = pydantic_named_types( @@ -291,7 +291,7 @@ def create( class UnionDecoder: """A Utility base class for decoding from a union of types.""" - loader: UnionWrapper + loader: Type[UnionWrapper[Any]] def __init__( self, @@ -300,7 +300,7 @@ def __init__( module_names: str | Sequence[str] = "", modules: Optional[Sequence[Any]] = None, explicit_types: Optional[Sequence[Any]] = None, - type_name_regex: Optional[re.Pattern] = None, + type_name_regex: Optional[re.Pattern[str]] = None, ) -> None: self.loader = UnionWrapper.create( model_name=model_name, @@ -313,19 +313,20 @@ def __init__( class CacDecoder(UnionDecoder): TYPE_NAME_REGEX = re.compile(r".*\.cac\.gt") - loader: UnionWrapper + loader: Type[UnionWrapper[Any]] def __init__( self, model_name: str, - type_name_regex: Optional[re.Pattern] = TYPE_NAME_REGEX, + type_name_regex: Optional[re.Pattern[str]] = TYPE_NAME_REGEX, **kwargs: Any, ) -> None: super().__init__(model_name, type_name_regex=type_name_regex, **kwargs) def decode( - self, cac_dict: dict, *, allow_missing: bool = True + self, cac_dict: dict[str, Any], *, allow_missing: bool = True ) -> ComponentAttributeClassGt: + decoded: ComponentAttributeClassGt try: decoded = self.loader.model_validate({"Wrapped": cac_dict}).Wrapped if not isinstance(decoded, ComponentAttributeClassGt): @@ -349,14 +350,15 @@ class ComponentDecoder(UnionDecoder): def __init__( self, model_name: str, - type_name_regex: Optional[re.Pattern] = TYPE_NAME_REGEX, + type_name_regex: Optional[re.Pattern[str]] = TYPE_NAME_REGEX, **kwargs: Any, ) -> None: super().__init__(model_name, type_name_regex=type_name_regex, **kwargs) def decode( - self, component_dict: dict, *, allow_missing: bool = True + self, component_dict: dict[str, Any], *, allow_missing: bool = True ) -> ComponentGt: + decoded: ComponentGt try: # Pydantic requires that our union of types (components here) be in # a named field, which by convention we call "Wrapped". diff --git a/src/gwproto/message.py b/src/gwproto/message.py index 62554d01..414a6a8e 100644 --- a/src/gwproto/message.py +++ b/src/gwproto/message.py @@ -1,6 +1,16 @@ # ruff: noqa: ANN401 - -from typing import Any, Callable, Generic, Literal, Mapping, Optional, TypeVar, Union +import typing +from typing import ( + Any, + Callable, + Generic, + Literal, + Mapping, + Optional, + TypeAlias, + TypeVar, + Union, +) from pydantic import BaseModel @@ -31,22 +41,25 @@ class Header(BaseModel): PAYLOAD_TYPE_FIELDS = ["TypeName", "type_alias", "TypeName", "type_name"] -GRIDWORKS_ENVELOPE_TYPE = "gw" +GRIDWORKS_ENVELOPE_TYPE: str = "gw" -def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict) -> None: +def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict[str, Any]) -> None: if arg_name not in kwargs_dict: payload = kwargs_dict.get("Payload") if payload is None or not hasattr(payload, arg_name): kwargs_dict[arg_name] = default_value +HeaderAlias: TypeAlias = Header + + class Message(BaseModel, Generic[PayloadT]): Header: Header Payload: PayloadT - TypeName: Literal["gw"] = GRIDWORKS_ENVELOPE_TYPE + TypeName: Literal["gw"] = GRIDWORKS_ENVELOPE_TYPE # type: ignore[assignment] - def __init__(self, header: Optional[Header] = None, **kwargs: Any) -> None: + def __init__(self, header: Optional[HeaderAlias] = None, **kwargs: Any) -> None: if header is None: header = self._header_from_kwargs(kwargs) super().__init__(Header=header, **kwargs) @@ -59,13 +72,13 @@ def src(self) -> str: @classmethod def type_name(cls) -> str: - return Message.model_fields["TypeName"].default + return typing.cast(str, Message.model_fields["TypeName"].default) def mqtt_topic(self) -> str: return MQTTTopic.encode(self.type_name(), self.src(), self.message_type()) @classmethod - def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> Header: + def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> HeaderAlias: header_kwargs = {} if "Payload" in kwargs: payload = kwargs["Payload"] diff --git a/src/gwproto/property_format.py b/src/gwproto/property_format.py index ff8fe5eb..038675cd 100644 --- a/src/gwproto/property_format.py +++ b/src/gwproto/property_format.py @@ -1,76 +1,15 @@ # ruff: noqa: ANN401 -import re import struct import uuid from datetime import datetime, timezone -from typing import Annotated, Any, Callable, List +from typing import Annotated, List -import pydantic from pydantic import BeforeValidator, Field UTC_2000_01_01_TIMESTAMP = datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() UTC_3000_01_01_TIMESTAMP = datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() -def predicate_validator( - field_name: str, - predicate: Callable[[Any], bool], - error_format: str = "", - **kwargs: dict[str, Any], -) -> classmethod: - """ - Produce a pydantic validator from a function returning a bool. - - Example: - - from typing import Any - from pydantic import BaseModel, ValidationError - from gwproto.property_format import predicate_validator - - def is_truthy(v: Any) -> bool: - return bool(v) - - class Foo(BaseModel): - an_int: int - - _validate_an_int = predicate_validator("an_int", is_truthy) - - print(Foo(an_int=1)) - - try: - print(Foo(an_int=0)) - except ValidationError as e: - print(e) - - Args: - field_name: the name of the field to validate. - predicate: the validation function. A truthy return value indicates success. - error_format: Optional format string for use in exception raised by validation failure. Takes one parameter, 'v'. - **kwargs: Passed to pydantic.validator() - - Returns: - The passed in object v. - """ - - def _validator(v: Any) -> Any: - if not predicate(v): - if error_format: - err_str = error_format.format(value=v) - else: - err_str = f"Failure of predicate on [{v}] with predicate {predicate}" - raise ValueError(err_str) - return v - - return pydantic.field_validator(field_name, **kwargs)(_validator) - - -MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") - - -def has_mac_address_format(mac_str: str) -> bool: - return bool(MAC_REGEX.match(mac_str.lower())) - - def is_short_integer(candidate: int) -> bool: try: struct.pack("h", candidate) diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index b9e11ffc..e168fe61 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -1,6 +1,7 @@ from typing import Literal, Optional import yarl +from pydantic_extra_types.mac_address import MacAddress from gwproto.types.component_gt import ComponentGt from gwproto.types.hubitat_gt import HubitatGt @@ -35,7 +36,7 @@ def make_stub(cls, component_id: str) -> "HubitatComponentGt": Host="", MakerApiId=-1, AccessToken="", - MacAddress="000000000000", + MacAddress=MacAddress("00:00:00:00:00:00"), ), ) diff --git a/src/gwproto/types/hubitat_gt.py b/src/gwproto/types/hubitat_gt.py index 03210548..72cc2a73 100644 --- a/src/gwproto/types/hubitat_gt.py +++ b/src/gwproto/types/hubitat_gt.py @@ -1,22 +1,38 @@ +import typing from typing import Optional import yarl from pydantic import BaseModel, ConfigDict +from pydantic_extra_types.mac_address import MacAddress -from gwproto.property_format import has_mac_address_format, predicate_validator from gwproto.types.rest_poller_gt import URLArgs, URLConfig +class URLConfigWithUrlArgs(URLConfig): + """ + A URLConfig with non-None url_args + """ + + url_args: URLArgs + + +class URLConfigForMakerAPI(URLConfigWithUrlArgs): + """ + A URLConfig with non-None url_args and url_path_args suitable for creating + a MakerAPI url. + """ + + url_path_args: dict[str, str | int | float] + + class HubitatGt(BaseModel): Host: str MakerApiId: int AccessToken: str - MacAddress: str + MacAddress: MacAddress WebListenEnabled: bool = True model_config = ConfigDict(extra="allow", populate_by_name=True) - _is_mac_address = predicate_validator("MacAddress", has_mac_address_format) - @property def listen_path(self) -> str: return self.MacAddress.replace(":", "-") @@ -24,26 +40,29 @@ def listen_path(self) -> str: def listen_url(self, url: yarl.URL) -> yarl.URL: return url / self.listen_path - def url_config(self) -> URLConfig: - return URLConfig( + def url_config(self) -> URLConfigWithUrlArgs: + return URLConfigWithUrlArgs( url_args=URLArgs( scheme="http", host=self.Host, ), ) - def maker_api_url_config(self) -> URLConfig: + def maker_api_url_config(self) -> URLConfigForMakerAPI: config = self.url_config() + if config.url_args is None: + raise ValueError( + f"ERROR. URLConfig.url_args ({config.url_args})" + " are insufficient to create MakerAPI url" + ) if config.url_args.query is None: config.url_args.query = [] config.url_args.query.append(("access_token", self.AccessToken)) - if config.url_path_format is None: - config.url_path_format = "" config.url_path_format += "/apps/api/{app_id}" if config.url_path_args is None: config.url_path_args = {} config.url_path_args.update({"app_id": self.MakerApiId}) - return config + return typing.cast(URLConfigForMakerAPI, config) def devices_url_config(self) -> URLConfig: config = self.maker_api_url_config() @@ -70,4 +89,10 @@ def refresh_url_config(self, device_id: int) -> URLConfig: return config def refresh_url(self, device_id: int) -> yarl.URL: - return URLConfig.make_url(self.refresh_url_config(device_id)) + url = URLConfig.make_url(self.refresh_url_config(device_id)) + if url is None: + raise ValueError( + f"ERROR. refreshed URL could not produce URL from <{self}> " + f"and device_id {device_id}" + ) + return url diff --git a/src/gwproto/types/rest_poller_gt.py b/src/gwproto/types/rest_poller_gt.py index 7c9c9512..d28c134d 100644 --- a/src/gwproto/types/rest_poller_gt.py +++ b/src/gwproto/types/rest_poller_gt.py @@ -4,7 +4,7 @@ """ from functools import cached_property -from typing import Literal, Optional, Self, Tuple +from typing import Any, Literal, Optional, Self, Tuple import yarl from pydantic import BaseModel, ConfigDict, HttpUrl, model_validator @@ -27,7 +27,7 @@ class URLArgs(BaseModel): model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) @classmethod - def dict_from_url(cls, url: str | yarl.URL) -> dict: + def dict_from_url(cls, url: str | yarl.URL) -> dict[str, Any]: if isinstance(url, str): url = yarl.URL(url) return { @@ -81,10 +81,15 @@ class URLConfig(BaseModel): model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) def to_url(self) -> yarl.URL: - return self.make_url(self) + url = self.make_url(self) + if url is None: + raise ValueError("URL cannot be None") + return url @classmethod - def make_url_args(cls, url_config: "URLConfig") -> Optional[dict]: + def make_url_args( + cls, url_config: Optional["URLConfig"] + ) -> Optional[dict[str, Any]]: if url_config is None: return None @@ -92,7 +97,7 @@ def make_url_args(cls, url_config: "URLConfig") -> Optional[dict]: if url_config.url is None: url_args = {} else: - url_args = dict(URLArgs.from_url(yarl.URL(url_config.url))) + url_args = dict(URLArgs.from_url(yarl.URL(str(url_config.url)))) # args from self.url_args if url_config.url_args is not None: @@ -108,7 +113,7 @@ def make_url_args(cls, url_config: "URLConfig") -> Optional[dict]: return url_args @classmethod - def make_url(cls, url_config: "URLConfig") -> Optional[yarl.URL]: + def make_url(cls, url_config: Optional["URLConfig"]) -> Optional[yarl.URL]: args = URLConfig.make_url_args(url_config) if args: return yarl.URL.build(**args) @@ -134,10 +139,10 @@ class SessionArgs(BaseModel): class RequestArgs(BaseModel): url: Optional[URLConfig] = None method: Literal["GET", "POST", "PUT", "DELETE"] = "GET" - params: Optional[dict] = None - data: Optional[dict | list | tuple] = None - headers: Optional[dict] = None - timeout: AioHttpClientTimeout = None + params: Optional[dict[str, Any]] = None + data: Optional[dict[str, Any] | list[Any] | tuple[Any]] = None + headers: Optional[dict[str, Any]] = None + timeout: Optional[AioHttpClientTimeout] = None ssl: Optional[bool] = None model_config = ConfigDict( extra="allow", alias_generator=snake_to_camel, populate_by_name=True @@ -176,28 +181,23 @@ class RESTPollerSettings(BaseModel): ignored_types=(cached_property,), ) - def url_args(self) -> dict: + def url_args(self) -> dict[str, Any]: session_args = URLConfig.make_url_args(self.session.base_url) request_args = URLConfig.make_url_args(self.request.url) - if ( - session_args is None - and request_args is None - and session_args is None - and request_args is None - ): - raise ValueError("Neither session.base_url nor request.url produces a URL") if session_args is not None: url_args = session_args if request_args is not None: url_args.update(request_args) else: + if request_args is None: + raise ValueError( + "Neither session.base_url nor request.url produces a URL" + ) url_args = request_args return url_args @cached_property def url(self) -> yarl.URL: - if self.session.base_url is None and self.request.url is None: - raise ValueError("Neither session.base_url nor request.url produces a URL") session_args = URLConfig.make_url_args(self.session.base_url) request_args = URLConfig.make_url_args(self.request.url) if session_args is not None: @@ -205,6 +205,10 @@ def url(self) -> yarl.URL: if request_args is not None: url_args.update(request_args) else: + if request_args is None: + raise ValueError( + "Neither session.base_url nor request.url produces a URL" + ) url_args = request_args return yarl.URL.build(**url_args) @@ -219,7 +223,7 @@ def post_model_validator(self) -> Self: raise ValueError( "ERROR. At least one of session.base_url and request.url must be specified" ) - if base_url is None and not url.is_absolute(): + if base_url is None and (url is None or not url.is_absolute()): raise ValueError( "ERROR. if session.base_url is None, request.url must be absolute\n" f" request.url: <{url}>\n" diff --git a/tests/types/test_hubitat_gt.py b/tests/types/test_hubitat_gt.py index 740caa30..7853d546 100644 --- a/tests/types/test_hubitat_gt.py +++ b/tests/types/test_hubitat_gt.py @@ -1,6 +1,7 @@ """Test HubitatGt""" import yarl +from pydantic_extra_types.mac_address import MacAddress from gwproto.types.hubitat_gt import HubitatGt @@ -8,13 +9,13 @@ def test_hubitat_gt() -> None: """Test HubitatGt""" - mac = "00:01:02:03:0A:0B" + mac = "00:01:02:03:0A:0B".lower() listen_path_exp = mac.replace(":", "-") h = HubitatGt( Host="192.168.1.10", MakerApiId=1, AccessToken="foo", - MacAddress=mac, + MacAddress=MacAddress(mac), ) assert h.WebListenEnabled is True assert h.listen_path == listen_path_exp From 6ac1c5f001ec9b15d25c15a388b6d1167dd3545b Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:24:55 -0400 Subject: [PATCH 115/168] Add gw --- poetry.lock | 57 +++++++++++++++++++++++++++++++------------------- pyproject.toml | 1 + 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index f351e97e..d5e8422d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -378,6 +378,21 @@ pygments = ">=2.7" sphinx = ">=6.0,<9.0" sphinx-basic-ng = ">=1.0.0.beta2" +[[package]] +name = "gridworks" +version = "1.2.0" +description = "Gridworks" +optional = false +python-versions = "<4.0,>=3.11" +files = [ + {file = "gridworks-1.2.0-py3-none-any.whl", hash = "sha256:ca3f1e6a6524af3f8ade539464fa98646ed4959fcf706f41d24910920e51bfae"}, + {file = "gridworks-1.2.0.tar.gz", hash = "sha256:2def4fba9b56c4e9049ed5a15ae1d68cbf8e3b603be14b9fcc5ab8a1014adbc2"}, +] + +[package.dependencies] +pydantic = ">=2.8.0,<3.0.0" +python-dotenv = ">=1.0.0" + [[package]] name = "h11" version = "0.14.0" @@ -1230,6 +1245,20 @@ 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-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 = "2024.2" @@ -1383,51 +1412,37 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, @@ -2128,4 +2143,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "f2c126344fd53b7ab7687b7cd6cd7923b7da283f71404bc5bf2a5d6065a4ee76" +content-hash = "564ab1d2944df2b18e919dcda17b83ee871fbb44b7e4ed7fdc4cd6c736ca8801" diff --git a/pyproject.toml b/pyproject.toml index 2d44444b..9b1c42ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ pydantic = "^2.8.2" yarl = "^1.9.2" pytz = "^2024.1" pydantic-extra-types = "^2.9.0" +gridworks = "^1.2.0" [tool.poetry.group.dev.dependencies] Pygments = ">=2.10.0" From 9de9fece2864a979a053ab0a2659c1d7c7b3b24b Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:25:17 -0400 Subject: [PATCH 116/168] Update code gen, gitignore --- .gitignore | 2 +- .../GridworksCore/Enum/DeriveEnums.xslt | 134 +- .../Enum/Docs/DeriveAslDocs.xslt | 62 +- .../Enum/Docs/Toc/DeriveToc.xslt | 2 +- .../Enum/EnumInit/DeriveEnumInit.xslt | 9 +- .../Enum/EnumTest/DeriveEnumTests.xslt | 51 +- .../Enum/Literal/DeriveLiteral.xslt | 117 ++ .../Enum/OldSchool/DeriveOldSchool.xslt | 111 ++ .../GridworksCore/Types/DeriveTypes.xslt | 1664 +++-------------- .../DerivePropertyFormats.xslt | 323 ++++ .../Types/TypeInit/DeriveTypeInit.xslt | 60 +- .../Types/TypeTests/DeriveTypeTests.xslt | 290 +-- CodeGenerationTools/GridworksCore/clean.sh | 21 +- 13 files changed, 1008 insertions(+), 1838 deletions(-) create mode 100644 CodeGenerationTools/GridworksCore/Enum/Literal/DeriveLiteral.xslt create mode 100644 CodeGenerationTools/GridworksCore/Enum/OldSchool/DeriveOldSchool.xslt create mode 100644 CodeGenerationTools/GridworksCore/Types/PropertyFormats/DerivePropertyFormats.xslt diff --git a/.gitignore b/.gitignore index 34b09407..841a72fa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ __pycache__/ .DS_Store hacking .idea -scratch.* +scratch* cook.sh .vscode diff --git a/CodeGenerationTools/GridworksCore/Enum/DeriveEnums.xslt b/CodeGenerationTools/GridworksCore/Enum/DeriveEnums.xslt index fe1c6cf8..62c52df4 100644 --- a/CodeGenerationTools/GridworksCore/Enum/DeriveEnums.xslt +++ b/CodeGenerationTools/GridworksCore/Enum/DeriveEnums.xslt @@ -19,13 +19,13 @@ - + - + @@ -41,12 +41,13 @@ from enum import auto from typing import List +from typing import Optional -from fastapi_utils.enums import StrEnum +from gw.enums import GwStrEnum class -(StrEnum): +(GwStrEnum): """ @@ -62,8 +63,7 @@ class Enum version in the GridWorks Type registry. - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: + Used by multiple Application Shared Languages (ASLs). For more information: - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html# @@ -77,13 +77,12 @@ class - Values (with symbols in parens): + Values: - - ( - ) + : @@ -116,10 +115,10 @@ class - + - + @@ -133,51 +132,31 @@ class ": """ Returns default value (in this case - + - + ) """ return cls. - + - + @classmethod - def values(cls) -> List[str]: - """ - Returns enum choices - """ - return [elt.value for elt in cls] - - @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "" if not isinstance(value, str): - raise ValueError(f"This method applies to strings, not enums") - if value not in value_to_version.keys(): + raise TypeError("This method applies to strings, not enums") + if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -199,87 +178,16 @@ class " - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of " - ". - """ - if symbol not in symbol_to_value.keys(): - return cls.default().value - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a - enum to send in seriliazed messages. - - Args: - symbol (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of " - ". - """ - if value not in value_to_symbol.keys(): - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - - - - "", - - - - ] - - -symbol_to_value = { - - - - "": " - - - - - - -", - - -} - -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { " - + - + ": " ", diff --git a/CodeGenerationTools/GridworksCore/Enum/Docs/DeriveAslDocs.xslt b/CodeGenerationTools/GridworksCore/Enum/Docs/DeriveAslDocs.xslt index a87fafac..6194dbfd 100644 --- a/CodeGenerationTools/GridworksCore/Enum/Docs/DeriveAslDocs.xslt +++ b/CodeGenerationTools/GridworksCore/Enum/Docs/DeriveAslDocs.xslt @@ -1,4 +1,4 @@ - + @@ -23,7 +23,7 @@ - + @@ -32,7 +32,7 @@ - Always + Never Always @@ -46,27 +46,28 @@ { - "gtr_asl": "001", - "enum_name": " + "gtr_asl": "001", + "enum_name": " ", - "enum_version": "", - "description": " + "enum_version": "", + "description": " ", - "url": " + "url": " ", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html# + "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html# ", - "values": [ + "values": [ " - " + + " , @@ -74,46 +75,50 @@ ], - "value_to_gt_symbol": { + "value_to_symbol": { - " - + + " + - + ": " " - , + , - }, - "value_to_version": { + + }, + "value_to_version": { - " - + + " + - + ": " " - , + , - }, - "value_descriptions": { + + }, + "value_descriptions": { - "": " + "": " More Info: @@ -125,10 +130,11 @@ - }, - "default_value": " + }, + "default_value": " " -} +} + diff --git a/CodeGenerationTools/GridworksCore/Enum/Docs/Toc/DeriveToc.xslt b/CodeGenerationTools/GridworksCore/Enum/Docs/Toc/DeriveToc.xslt index 2af81e27..41cd8b7a 100644 --- a/CodeGenerationTools/GridworksCore/Enum/Docs/Toc/DeriveToc.xslt +++ b/CodeGenerationTools/GridworksCore/Enum/Docs/Toc/DeriveToc.xslt @@ -1,4 +1,4 @@ - + diff --git a/CodeGenerationTools/GridworksCore/Enum/EnumInit/DeriveEnumInit.xslt b/CodeGenerationTools/GridworksCore/Enum/EnumInit/DeriveEnumInit.xslt index 03ff227e..cebc826a 100644 --- a/CodeGenerationTools/GridworksCore/Enum/EnumInit/DeriveEnumInit.xslt +++ b/CodeGenerationTools/GridworksCore/Enum/EnumInit/DeriveEnumInit.xslt @@ -54,10 +54,9 @@ on these ideas: - [GridWorks Enums](https://gridwork-type-registry.readthedocs.io/en/latest/types.html) - [GridWorks Types](https://gridwork-type-registry.readthedocs.io/en/latest/types.html) - [ASLs](https://gridwork-type-registry.readthedocs.io/en/latest/asls.html) - - - """ - + """ + + from gwproto.enums. @@ -84,7 +83,7 @@ __all__ = [ - ", # [ + ", # [ . ](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html# diff --git a/CodeGenerationTools/GridworksCore/Enum/EnumTest/DeriveEnumTests.xslt b/CodeGenerationTools/GridworksCore/Enum/EnumTest/DeriveEnumTests.xslt index aa62905b..1888b651 100644 --- a/CodeGenerationTools/GridworksCore/Enum/EnumTest/DeriveEnumTests.xslt +++ b/CodeGenerationTools/GridworksCore/Enum/EnumTest/DeriveEnumTests.xslt @@ -24,7 +24,7 @@ - + @@ -42,6 +42,7 @@ Tests for enum . from the GridWorks Type Registry. """ + from gwproto.enums import @@ -50,56 +51,68 @@ def test_ assert set(.values()) == { - + + + " - - - + ", + + + + + , + + + + " - + ", + + - ", } assert .default() == . - + + - - + + - + + assert .enum_name() == " - " + " + + + assert .enum_version() == " " + assert .version(" - + - + ") == " " - - - for value in .values(): - symbol = .value_to_symbol(value) - assert .symbol_to_value(symbol) == value + diff --git a/CodeGenerationTools/GridworksCore/Enum/Literal/DeriveLiteral.xslt b/CodeGenerationTools/GridworksCore/Enum/Literal/DeriveLiteral.xslt new file mode 100644 index 00000000..1af66e94 --- /dev/null +++ b/CodeGenerationTools/GridworksCore/Enum/Literal/DeriveLiteral.xslt @@ -0,0 +1,117 @@ + + + + + + + + + ' + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../src/gwproto/enums/ + .py + + Always + + + + +# Literal Enum: +# - no additional values can be added over time. +# - Sent as-is, not in hex symbol +from enum import auto +from typing import List + +from gw.enums import GwStrEnum + + +class +(GwStrEnum): + """ + + + + + + + + + + """ + + + + + + + + + + + + + = auto() + + + + + + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] + + @classmethod + def default(cls) -> " + + ": + return cls. + + + + + @classmethod + def enum_name(cls) -> str: + return " + + " + + + + + + + + + + + + + + + diff --git a/CodeGenerationTools/GridworksCore/Enum/OldSchool/DeriveOldSchool.xslt b/CodeGenerationTools/GridworksCore/Enum/OldSchool/DeriveOldSchool.xslt new file mode 100644 index 00000000..dc9ff840 --- /dev/null +++ b/CodeGenerationTools/GridworksCore/Enum/OldSchool/DeriveOldSchool.xslt @@ -0,0 +1,111 @@ + + + + + + + + + ' + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../src/gwproto/enums/ + .py + + Always + + + + +"""Old School enum (uses integers)""" +from enum import Enum +from typing import List + + +class +(Enum): + """ + + + + + + + + + + """ + + + + + + + + + + + = + + + + + + @classmethod + def values(cls) -> List[str]: + """ + Returns enum choices + """ + return [elt.value for elt in cls] + + @classmethod + def default(cls) -> " + + ": + return cls. + + + + + @classmethod + def enum_name(cls) -> str: + return " + + " + + + + + + + + + + + + + + + diff --git a/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt b/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt index 4571ae25..5299be4a 100644 --- a/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt +++ b/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt @@ -38,7 +38,7 @@ - Never + Always Always @@ -55,67 +55,61 @@ """Type , version """ -import json -import logging -from typing import Any -from typing import Dict - - -from typing import List - - -from typing import Literal - - -from typing import Optional +from typing import Any, Literal + +, List - -from pydantic import BaseModel -from pydantic import Field - - -from pydantic import root_validator - - - -from pydantic import validator + +, Optional + - - - -from gwproto.data_classes. - - - - import +from pydantic import BaseModel + + +, field_validator + +, model_validator - - -from gwproto.data_classes.components. - - - - import + +, PositiveInt + +, ConfigDict - + -from gwproto.data_classes.cacs. - - - - import +from typing_extensions import Self + + + + +from gw import check_is_market_slot_name_lrd_format + + @@ -129,15 +123,6 @@ from gwproto.types. - -from gwproto.types. - - - - import - - -_Maker @@ -151,29 +136,80 @@ from gwproto.types. - from gwproto.enums import - - as Enum - + - - + + -from gwproto.errors import SchemaError +import algosdk + +from gwproto.property_format import ( + + + + + + UUID4Str, + + + + SpaceheatName, + + + + LeftRightDotStr, + + + + HandleName, + + + + HexChar, + + + + UTCMilliseconds, + + + + UTCSeconds, + + + + + + + check_is_ + + + + , + + + + +) + -LOG_FORMAT = ( - "%(levelname) -10s %(asctime)s %(name) -30s %(funcName) " - "-35s %(lineno) -5d: %(message)s" -) -LOGGER = logging.getLogger(__name__) + + +from gwproto.property_format import ReallyAnInt + + class @@ -194,11 +230,12 @@ class - + + @@ -208,28 +245,24 @@ class """ - + + - + Id - - - - Enum - - - - - + + + + @@ -245,18 +278,51 @@ class - - - + + + + + UUID4Str + + + SpaceheatName + + + LeftRightDotStr + + + HandleName + + + HexChar + + + UTCMilliseconds + + + UTCSeconds + + + PositiveInt + + + ReallyAnInt + + + + + + + - + - str + UUID4Str @@ -274,7 +340,7 @@ class - ] + ] = None @@ -285,98 +351,7 @@ class - - - = - -Field( - title=" - - - - - - - ", - - - - - - - - - description=( - " - - - - - " - - - "[More info]( - - )" - - - ), - - - - - - - description=" - - ", - - - - - - description=" - [More info]( - - )", - - - - - - - - - - - - - - - - - default=None, - - - - - - default= - - - - . - - - , - - - - - - - ) - + @@ -385,6 +360,15 @@ class Version: Literal[" "] = "" + + + model_config = ConfigDict( + extra="allow" + ) + + + + @@ -394,11 +378,8 @@ class - + - - Enum - @@ -424,7 +405,7 @@ class - + @@ -445,30 +426,41 @@ class ] - + ] - + - + Id - - + - @validator("" + @field_validator("" - , pre=True + , mode="before" ) + @classmethod def @@ -550,14 +542,22 @@ class - ... - # TODO: Implement Axiom(s) + # Implement Axiom(s) - + try: check_is_ @@ -572,86 +572,110 @@ class raise ValueError( f" failed - format validation: {e}" - ) + format validation: {e}", + ) from e raise ValueError(f" failed - format validation: {e}") + format validation: {e}") from e - - return v - + - for elt in v: - try: + try: + for elt in v: check_is_ (elt) - except ValueError as e: - raise ValueError( - f" element {elt} failed - - format validation: {e}" - ) - return v + except ValueError as e: + raise ValueError( + f" element failed + + format validation: {e}", + ) from e try: - check_is_uuid_canonical_textual(v) + is_uuid4_str(v) except ValueError as e: raise ValueError( f" - Id failed UuidCanonicalTextual format validation: {e}" - ) - return v + Id failed UUID4Str format validation: {e}", + ) from e - for elt in v: - try: - check_is_uuid_canonical_textual(elt) - except ValueError as e: - raise ValueError( - f" element {elt} failed - - format validation: {e}" - ) - return v + try: + for elt in v: + is_uuid4_str(elt) + except ValueError as e: + raise ValueError( + f" element failed + + format validation: {e}", + ) from e - + + + + + return v + + + - @root_validator + @model_validator - (pre=True) + (mode="before") + + + (mode="after") - def check_axiom_(cls, v: dict) -> dict: + def check_axiom_(self) -> Self: """ Axiom : @@ -666,178 +690,95 @@ class """ - # TODO: Implement check for axiom " - return v + # Implement check for axiom " + return self - - - - - - - - - - - def as_dict(self) -> Dict[str, Any]: - """ - Translate the object into a dictionary representation that can be serialized into a - object. - - This method prepares the object for serialization by the as_type method, creating a - dictionary with key-value pairs that follow the requirements for an instance of the - - type. Unlike the standard python dict method, - it makes the following substantive changes: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - """ - d = { - key: value - for key, value in self.dict( - include=self.__fields_set__ | {"TypeName", "Version"} - ).items() - if value is not None - } + def model_dump(self, **kwargs: dict[str, Any]) -> dict: + d = super().model_dump(**kwargs) - - - - - - - - - Enum - - - - - + - + - del d[""] - d[" - - - - GtEnumSymbol"] = - .value_to_symbol(self.) + d[""] = self. + + .value - + - del d[""] - - - - = [] - for elt in self. - : - - - - .append( - .value_to_symbol(elt.value)) d[" - "] = - - - + "] = [elt.value for elt in self. + + ] - + d[" "] = self. - - .as_dict() + + .model_dump(**kwargs) - + - # Recursively calling as_dict() - - - - - = [] - for elt in self. - - : - - - - - .append(elt.as_dict()) d[" - - "] = - - - + + "] = [elt.model_dump(**kwargs) for elt in self. + + ] - + - + if " - " in d.keys(): - del d[""] - d[" - - - - GtEnumSymbol"] = - .value_to_symbol(self.) + " in d: + d[""] = d[" + "].value - + if " - " in d.keys(): + " in d: del d[""] = [] for elt in self. - : + : - .append( - .value_to_symbol(elt.value)) + .append(elt.value) d[" "] = @@ -845,37 +786,37 @@ class - + if " - " in d.keys(): + " in d: del d[""] d[" "] = self. - .as_dict() + .model_dump(**kwargs) - + if " - " in d.keys(): + " in d: = [] for elt in self. - + : - .append(elt.as_dict()) + .append(elt.model_dump(**kwargs)) d[" "] = @@ -895,1040 +836,9 @@ class return d - def as_type(self) -> bytes: - """ - Serialize to the - representation. - - Instances in the class are python-native representations of - objects, while the actual object is the serialized UTF-8 byte - string designed for sending in a message. - - This method calls the as_dict() method, which differs from the native python dict() - in the following key ways: - - Enum Values: Translates between the values used locally by the actor to the symbol - sent in messages. - - - Removes any key-value pairs where the value is None for a clearer message, especially - in cases with many optional attributes. - - It also applies these changes recursively to sub-types. - - Its near-inverse is - - .type_to_tuple(). If the type (or any sub-types) - includes an enum, then the type_to_tuple will map an unrecognized symbol to the - default enum value. This is why these two methods are only 'near' inverses. - """ - json_string = json.dumps(self.as_dict()) - return json_string.encode("utf-8") - - def __hash__(self): - return hash((type(self),) + tuple(self.__dict__.values())) # noqa - - -class - - _Maker: - type_name = "" - version = "" - - def __init__( - self, - - - - - - - - - - Id - - - - - - - Enum - - - - - - - - - - - - Optional[ - - - - - List[ - - - - - - - - - - - - - - - str - - - - - - - - - - - - - ] - - - - - ] - - - - - - - - - : - - - - - - , - - - , - - - - - - - - ): - self.tuple = - ( - - - - - - - - Id - - - - = - - - - - - - - , - - - , - - - - - - - ) - - @classmethod - def tuple_to_type(cls, tuple: - ) -> bytes: - """ - Given a Python class object, returns the serialized JSON type object. - """ - return tuple.as_type() - - @classmethod - def type_to_tuple(cls, t: bytes) -> -: - """ - Given a serialized JSON type object, returns the Python class object. - """ - try: - d = json.loads(t) - except TypeError: - raise SchemaError("Type must be string or bytes!") - if not isinstance(d, dict): - raise SchemaError(f"Deserializing <{t}> must result in dict!") - return cls.dict_to_tuple(d) - - @classmethod - def dict_to_tuple(cls, d: dict[str, Any]) -> - : - """ - Deserialize a dictionary representation of a - message object - into a - python object for internal use. - - This is the near-inverse of the - .as_dict() method: - - Enums: translates between the symbols sent in messages between actors and - the values used by the actors internally once they've deserialized the messages. - - Types: recursively validates and deserializes sub-types. - - Note that if a required attribute with a default value is missing in a dict, this method will - raise a SchemaError. This differs from the pydantic BaseModel practice of auto-completing - missing attributes with default values when they exist. - - Args: - d (dict): the dictionary resulting from json.loads(t) for a serialized JSON type object t. - - Raises: - SchemaError: if the dict cannot be turned into a object. - - Returns: - - """ - d2 = dict(d) - - - - - - - - - - - Enum - - - - - - - - - - - - - - - - - - - if " - - - GtEnumSymbol" not in d2.keys(): - raise SchemaError(f" - - - - GtEnumSymbol missing from dict <{d2}>") - value = - - .symbol_to_value(d2[" - - - GtEnumSymbol"]) - d2[" - - "] = - - (value) - - - - - - if " - - " not in d2.keys(): - raise SchemaError(f"dict <{d2}> missing - - ") - if not isinstance(d2[" - - "], List): - raise SchemaError(" must be a List!") - - - - - = [] - for elt in d2[" - - "]: - value = - - .symbol_to_value(elt) - - - - - .append( - (value)) - d2[" - "] = - - - - - - - - - if "" not in d2.keys(): - raise SchemaError(f"dict missing : <{d2}>") - if not isinstance(d2[" - "], dict): - raise SchemaError(f" - - <{d2[' - ']}> must be a - - - - !") - - - - - = - - - - _Maker.dict_to_tuple(d2[" - - "]) - d2[" - "] = - - - - - - - - - if "" not in d2.keys(): - raise SchemaError(f"dict missing : <{d2}>") - if not isinstance(d2[" - "], List): - raise SchemaError(f" - - <{d2[' - ']}> must be a List!") - - - - - = [] - for elt in d2[" - "]: - if not isinstance(elt, dict): - raise SchemaError(f" - - <{d2[' - ']}> must be a List of - - - - types") - t = - - - - _Maker.dict_to_tuple(elt) - - - .append(t) - d2[" - "] = - - - - - - - - - - - - - - if "" in d2.keys(): - - - - - - - value = - - .symbol_to_value(d2[" - - - GtEnumSymbol"]) - d2[" - - "] = - - (value) - - - - if not isinstance(d2[" - "], dict): - raise SchemaError(f"d[' - - '] <{d2[' - ']}> must be a - - - - !") - - - - - = - - - - _Maker.dict_to_tuple(d2[" - - "]) - d2[" - "] = - - - - - - - - - - - - - - - - - - if " - - - Id - - - - " not in d2.keys(): - raise SchemaError(f"dict missing : <{d2}>") - - - - - - - - - - - if "TypeName" not in d2.keys(): - raise SchemaError(f"TypeName missing from dict <{d2}>") - if "Version" not in d2.keys(): - raise SchemaError(f"Version missing from dict <{d2}>") - if d2["Version"] != "": - LOGGER.debug( - f"Attempting to interpret - - version {d2['Version']} as version - " - ) - d2["Version"] = "" - return (**d2) - - - - @classmethod - def tuple_to_dc(cls, t: - ) -> : - if t. in - .by_id.keys(): - dc = .by_id[t. - ] - else: - dc = ( - - - - - - - - - - _id=t. - Id - - - - - - =t. - - - - - - - - , - - - , - - - - - - - - ) - return dc - - @classmethod - def dc_to_tuple(cls, dc: ) -> : - t = _Maker( - - - - - - - - - _id=dc. - - - - _id, - - - - - - - - =dc. - - - - - - - - - , - - - , - - - - - - - - - - - - ).tuple - return t - - @classmethod - def type_to_dc(cls, t: str) -> : - return cls.tuple_to_dc(cls.type_to_tuple(t)) - - @classmethod - def dc_to_type(cls, dc: ) -> str: - return cls.dc_to_tuple(dc).as_type() - @classmethod - def dict_to_dc(cls, d: dict[Any, str]) -> : - return cls.tuple_to_dc(cls.dict_to_tuple(d)) - - - - - - - - - - - - - - Enum - - - - - - - - - - - - - - - - - - - -def check_is_algo_address_string_format(v: str) -> None: - """ - AlgoAddressStringFormat format: The public key of a private/public Ed25519 - key pair, transformed into an Algorand address, by adding a 4-byte checksum - to the end of the public key and then encoding in base32. - - Raises: - ValueError: if not AlgoAddressStringFormat format - """ - import algosdk - at = algosdk.abi.AddressType() - try: - result = at.decode(at.encode(v)) - except Exception as e: - raise ValueError(f"Not AlgoAddressStringFormat: {e}") - - - - - - - -def check_is_algo_msg_pack_encoded(v: str) -> None: - """ - AlgoMSgPackEncoded format: the format of a transaction sent to - the Algorand blockchain. Error is not thrown with - algosdk.encoding.future_msg_decode(candidate) - - Raises: - ValueError: if not AlgoMSgPackEncoded format - """ - import algosdk - try: - algosdk.encoding.future_msgpack_decode(v) - except Exception as e: - raise ValueError(f"Not AlgoMsgPackEncoded format: {e}") - - - - - - -def check_is_bit(v: int) -> None: - """ - Checks Bit format - - Bit format: The value must be the integer 0 or the integer 1. - - Will not attempt to first interpret as an integer. For example, - 1.3 will not be interpreted as 1 but will raise an error. - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not 0 or 1 - """ - if not v in [0,1]: - raise ValueError(f"<{v}> must be 0 or 1") - - - - - - - -def check_is_hex_char(v: str) -> None: - """Checks HexChar format - - HexChar format: single-char string in '0123456789abcdefABCDEF' - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not HexChar format - """ - if not isinstance(v, str): - raise ValueError(f"<{v}> must be a hex char, but not even a string") - if len(v) > 1: - raise ValueError(f"<{v}> must be a hex char, but not of len 1") - if v not in "0123456789abcdefABCDEF": - raise ValueError(f"<{v}> must be one of '0123456789abcdefABCDEF'") - - - - - - - -def check_is_iso_format(v: str) -> None: - """ - Example: '2024-01-10T15:30:45.123456-05:00' The string does not - need to include microseconds. - """ - import datetime - - try: - datetime.datetime.fromisoformat(v.replace("Z", "+00:00")) - except: - raise ValueError(f"<{v}> is not IsoFormat") - - - - - - - -def check_is_left_right_dot(v: str) -> None: - """Checks LeftRightDot Format - - LeftRightDot format: Lowercase alphanumeric words separated by periods, with - the most significant word (on the left) starting with an alphabet character. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not LeftRightDot format - """ - from typing import List - - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - if not word.isalnum(): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric.") - if not v.islower(): - raise ValueError(f"All characters of <{v}> must be lowercase.") - - - - - - - - -def check_is_market_type_name_lrd_format(v: str) -> None: - from gwproto.enums import MarketTypeName - try: - x = v.split(".") - except AttributeError: - raise ValueError(f"<{v}> failed to split on '.'") - if not x[0] in MarketTypeName.values(): - raise ValueError(f"<{v}> not recognized MarketType") - g_node_alias = ".".join(x[1:]) - check_is_left_right_dot(g_node_alias) - - -def check_is_market_slot_name_lrd_format(v: str) -> None: - """ - MaketSlotNameLrdFormat: the format of a MarketSlotName. - - The first word must be a MarketTypeName - - The last word (unix time of market slot start) must - be a 10-digit integer divisible by 300 (i.e. all MarketSlots - start at the top of 5 minutes) - - More strictly, the last word must be the start of a - MarketSlot for that MarketType (i.e. divisible by 3600 - for hourly markets) - - The middle words have LeftRightDot format (GNodeAlias - of the MarketMaker) - Example: rt60gate5.d1.isone.ver.keene.1673539200 - - """ - from gwproto.data_classes.market_type import MarketType - try: - x = v.split(".") - except AttributeError: - raise ValueError(f"<{v}> failed to split on '.'") - slot_start = x[-1] - if len(slot_start) != 10: - raise ValueError(f"slot start {slot_start} not of length 10") - try: - slot_start = int(slot_start) - except ValueError: - raise ValueError(f"slot start {slot_start} not an int") - if slot_start % 300 != 0: - raise ValueError(f"slot start {slot_start} not a multiple of 300") - - market_type_name_lrd = ".".join(x[:-1]) - try: - check_is_market_type_name_lrd_format(market_type_name_lrd) - except ValueError as e: - raise ValueError(f"e") - - market_type = MarketType.by_id[market_type_name_lrd.split(".")[0]] - if not slot_start % (market_type.duration_minutes * 60) == 0: - raise ValueError( - f"market_slot_start_s mod {(market_type.duration_minutes * 60)} must be 0" - ) - - - - - - -def check_is_non_negative_integer(v: int) -> None: - """ - Must be non-negative when interpreted as an integer. Interpretation - as an integer follows the pydantic rules for this - which will round - down rational numbers. So 0 is fine, and 1.7 will be interpreted as - 1 and is also fine. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 0 - """ - v2 = int(v) - if v2 < 0: - raise ValueError(f"<{v}> is not NonNegativeInteger") - - - - - - - -def check_is_positive_integer(v: int) -> None: - """ - Must be positive when interpreted as an integer. Interpretation as an - integer follows the pydantic rules for this - which will round down - rational numbers. So 1.7 will be interpreted as 1 and is also fine, - while 0.5 is interpreted as 0 and will raise an exception. - - Args: - v (int): the candidate - - Raises: - ValueError: if v < 1 - """ - v2 = int(v) - if v2 < 1: - raise ValueError(f"<{v}> is not PositiveInteger") - - - - - - -def check_is_reasonable_unix_time_ms(v: int) -> None: - """Checks ReasonableUnixTimeMs format - - ReasonableUnixTimeMs format: unix milliseconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeMs format - """ - import pendulum - - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp * 1000 > v: # type: ignore[attr-defined] - raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp * 1000 < v: # type: ignore[attr-defined] - raise ValueError(f"<{v}> must be before Jan 1 3000") - - - - - - - - -def check_is_reasonable_unix_time_s(v: int) -> None: - """Checks ReasonableUnixTimeS format - - ReasonableUnixTimeS format: unix seconds between Jan 1 2000 and Jan 1 3000 - - Args: - v (int): the candidate - - Raises: - ValueError: if v is not ReasonableUnixTimeS format - """ - import pendulum - if pendulum.parse("2000-01-01T00:00:00Z").int_timestamp > v: # type: ignore[attr-defined] - raise ValueError(f"<{v}> must be after Jan 1 2000") - if pendulum.parse("3000-01-01T00:00:00Z").int_timestamp < v: # type: ignore[attr-defined] - raise ValueError(f"<{v}> must be before Jan 1 3000") - - - - - - - - -def check_is_spaceheat_name(v: str) -> None: - """Check SpaceheatName Format. - - Validates if the provided string adheres to the SpaceheatName format: - Lowercase words separated by periods, where word characters can be alphanumeric - or a hyphen, and the first word starts with an alphabet character. - - Args: - candidate (str): The string to be validated. - - Raises: - ValueError: If the provided string is not in SpaceheatName format. - """ - from typing import List - try: - x: List[str] = v.split(".") - except: - raise ValueError(f"Failed to seperate <{v}> into words with split'.'") - first_word = x[0] - first_char = first_word[0] - if not first_char.isalpha(): - raise ValueError( - f"Most significant word of <{v}> must start with alphabet char." - ) - for word in x: - for char in word: - if not (char.isalnum() or char == '-'): - raise ValueError(f"words of <{v}> split by by '.' must be alphanumeric or hyphen.") - if not v.islower(): - raise ValueError(f"<{v}> must be lowercase.") - - - - - - -def check_is_uuid_canonical_textual(v: str) -> None: - """Checks UuidCanonicalTextual format - - UuidCanonicalTextual format: A string of hex words separated by hyphens - of length 8-4-4-4-12. - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not UuidCanonicalTextual format - """ - try: - x = v.split("-") - except AttributeError as e: - raise ValueError(f"Failed to split on -: {e}") - if len(x) != 5: - raise ValueError(f"<{v}> split by '-' did not have 5 words") - for hex_word in x: - try: - int(hex_word, 16) - except ValueError: - raise ValueError(f"Words of <{v}> are not all hex") - if len(x[0]) != 8: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[1]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[2]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[3]) != 4: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - if len(x[4]) != 12: - raise ValueError(f"<{v}> word lengths not 8-4-4-4-12") - - - - - - - - -def check_is_world_instance_name_format(v: str) -> None: - """Checks WorldInstanceName Format - - WorldInstanceName format: A single alphanumerical word starting - with an alphabet char (the root GNodeAlias) and an integer, - seperated by '__'. For example 'd1__1' - - Args: - v (str): the candidate - - Raises: - ValueError: if v is not WorldInstanceNameFormat format - """ - try: - words = v.split("__") - except: - raise ValueError(f"<{v}> is not split by '__'") - if len(words) != 2: - raise ValueError(f"<{v}> not 2 words separated by '__'") - try: - int(words[1]) - except: - raise ValueError(f"<{v}> second word not an int") - - root_g_node_alias = words[0] - first_char = root_g_node_alias[0] - if not first_char.isalpha(): - raise ValueError(f"<{v}> first word must be alph char") - if not root_g_node_alias.isalnum(): - raise ValueError(f"<{v}> first word must be alphanumeric") - - - - - + def type_name_value(cls) -> str: + return "" diff --git a/CodeGenerationTools/GridworksCore/Types/PropertyFormats/DerivePropertyFormats.xslt b/CodeGenerationTools/GridworksCore/Types/PropertyFormats/DerivePropertyFormats.xslt new file mode 100644 index 00000000..3a026ced --- /dev/null +++ b/CodeGenerationTools/GridworksCore/Types/PropertyFormats/DerivePropertyFormats.xslt @@ -0,0 +1,323 @@ + + + + + + + + + ' + + + + + + + + + + + + + ../../../../src/gwproto/property_format.py + + Always + + + +# ruff: noqa: ANN401 +import re +import uuid +from datetime import datetime, timezone +from typing import Annotated, Any, Callable, List + +import pydantic +from pydantic import BeforeValidator, Field + +UTC_2000_01_01_TIMESTAMP = datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() +UTC_3000_01_01_TIMESTAMP = datetime(3000, 1, 1, tzinfo=timezone.utc).timestamp() + + +def predicate_validator( + field_name: str, + predicate: Callable[[Any], bool], + error_format: str = "", + **kwargs: dict[str, Any], +) -> classmethod: + """ + Produce a pydantic validator from a function returning a bool. + + Example: + + from typing import Any + from pydantic import BaseModel, ValidationError + from gwproto.property_format import predicate_validator + + def is_truthy(v: Any) -> bool: + return bool(v) + + class Foo(BaseModel): + an_int: int + + _validate_an_int = predicate_validator("an_int", is_truthy) + + print(Foo(an_int=1)) + + try: + print(Foo(an_int=0)) + except ValidationError as e: + print(e) + + Args: + field_name: the name of the field to validate. + predicate: the validation function. A truthy return value indicates success. + error_format: Optional format string for use in exception raised by validation failure. Takes one parameter, 'v'. + **kwargs: Passed to pydantic.validator() + + Returns: + The passed in object v. + """ + + def _validator(v: Any) -> Any: + if not predicate(v): + if error_format: + err_str = error_format.format(value=v) + else: + err_str = f"Failure of predicate on [{v}] with predicate {predicate}" + raise ValueError(err_str) + return v + + return pydantic.field_validator(field_name, **kwargs)(_validator) + + +def check_is_log_style_date_with_millis(v: str) -> None: + """Checks LogStyleDateWithMillis format + + LogStyleDateWithMillis format: YYYY-MM-DDTHH:mm:ss.SSS + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not LogStyleDateWithMillis format. + In particular the milliseconds must have exactly 3 digits. + """ + correct_millisecond_part_length = 3 + try: + datetime.fromisoformat(v) + except ValueError as e: + raise ValueError(f"{v} is not in LogStyleDateWithMillis format") from e + # The python fromisoformat allows for either 3 digits (milli) or 6 (micro) + # after the final period. Make sure its 3 + milliseconds_part = v.split(".")[1] + if len(milliseconds_part) != correct_millisecond_part_length: + raise ValueError( + f"{v} is not in LogStyleDateWithMillis format." + " Milliseconds must have exactly 3 digits" + ) + + +def is_handle_name(v: str) -> None: + """ + HandleName format: words separated by periods, where the worlds are lowercase + alphanumeric plus hyphens + """ + try: + x = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError( + f"Most significant word of <{v}> must start wif64th alphabet char." + ) + for word in x: + for char in word: + if not (char.isalnum() or char == "-"): + raise ValueError( + f"words of <{v}> split by by '.' must be alphanumeric or hyphen." + ) + if not v.islower(): + raise ValueError(f" <{v}> must be lowercase.") + return v + + +def is_hex_char(v: str) -> str: + """Checks HexChar format + + HexChar format: single-char string in '0123456789abcdefABCDEF' + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not HexChar format + """ + if not isinstance(v, str): + raise ValueError(f"<{v}> must be string. Got type <{type(v)}") # noqa: TRY004 + if len(v) > 1: + raise ValueError(f"<{v}> must be a hex char, but not of len 1") + if v not in "0123456789abcdefABCDEF": + raise ValueError(f"<{v}> must be one of '0123456789abcdefABCDEF'") + return v + + +def is_int(v: int) -> int: + if not isinstance(v, int): + raise TypeError("Not an integer!") + return v + + +def is_left_right_dot(candidate: str) -> str: + """Lowercase AlphanumericStrings separated by dots (i.e. periods), with most + significant word to the left. I.e. `d1.ne` is the child of `d1`. + Checking the format cannot verify the significance of words. All + words must be alphanumeric. Most significant word must start with + an alphabet charecter + + + Raises: + ValueError: if candidate is not of lrd format (e.g. d1.iso.me.apple) + """ + try: + x: List[str] = candidate.split(".") + except Exception as e: + raise ValueError("Failed to seperate into words with split'.'") from e + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError( + f"Most significant word must start with alphabet char. Got '{first_word}'" + ) + for word in x: + if not word.isalnum(): + raise ValueError( + f"words seperated by dots must be alphanumeric. Got '{word}'" + ) + if not candidate.islower(): + raise ValueError(f"alias must be lowercase. Got '{candidate}'") + return candidate + + + +MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") + + +def has_mac_address_format(mac_str: str) -> bool: + return bool(MAC_REGEX.match(mac_str.lower())) + + +def is_spaceheat_name(v: str) -> str: + """ + SpaceheatName format: Lowercase alphanumeric words separated by hypens + """ + try: + x = v.split("-") + except Exception as e: + raise ValueError( + f"<{v}>: Fails SpaceheatName format! Failed to seperate into words with split'-'" + ) from e + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! Most significant word must start with alphabet char." + ) + for word in x: + if not word.isalnum(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! words of split by by '-' must be alphanumeric." + ) + if not v.islower(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! All characters of must be lowercase." + ) + return v + + +def is_uuid4_str(v: str) -> str: + v = str(v) + try: + u = uuid.UUID(v) + except Exception as e: + raise ValueError(f"Invalid UUID4: <{v} <{e}>") from e + if u.version != 4: + raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") + return str(u) + + +def is_world_instance_name_format(candidate: str) -> bool: + try: + words = candidate.split("__") + except: # noqa + return False + if len(words) != 2: + return False + try: + int(words[1]) + except: # noqa + return False + try: + root_g_node_alias_words = words[0].split(".") + except: # noqa + return False + return not len(root_g_node_alias_words) > 1 + + +def check_is_ads1115_i2c_address(v: str) -> None: + """ + Ads1115I2cAddress: ToLower(v) in ['0x48', '0x49', '0x4a', '0x4b']. + + One of the 4 allowable I2C addresses for Texas Instrument Ads1115 chips. + + Raises: + ValueError: if not Ads1115I2cAddress format + """ + if v.lower() not in ["0x48", "0x49", "0x4a", "0x4b"]: + raise ValueError(f"Not Ads1115I2cAddress: <{v}>") + + +def check_is_near5(v: str) -> None: + """ + 4.5 <= v <= 5.5 + """ + min_pi_voltage = 4.5 + max_pi_voltage = 5.5 + if v < min_pi_voltage or v > max_pi_voltage: + raise ValueError(f"<{v}> is not between 4.5 and 5.5, not Near5") + + +def is_bit(candidate: int) -> int: + if candidate not in (0, 1): + raise ValueError(f"Candidate must be 0 or 1, Got {candidate}") + return candidate + + +Bit = Annotated[int, BeforeValidator(is_bit)] +HandleName = Annotated[str, BeforeValidator(is_handle_name)] +HexChar = Annotated[str, BeforeValidator(is_hex_char)] +LeftRightDotStr = Annotated[str, BeforeValidator(is_left_right_dot)] +SpaceheatName = Annotated[str, BeforeValidator(is_spaceheat_name)] +ReallyAnInt = Annotated[int, BeforeValidator(is_int)] +UUID4Str = Annotated[str, BeforeValidator(is_uuid4_str)] +UTCSeconds = Annotated[ + int, Field(ge=UTC_2000_01_01_TIMESTAMP, le=UTC_3000_01_01_TIMESTAMP) +] +UTCMilliseconds = Annotated[ + int, Field(ge=UTC_2000_01_01_TIMESTAMP * 1000, le=UTC_3000_01_01_TIMESTAMP * 1000) +] + + + + + + + + + + + + + + diff --git a/CodeGenerationTools/GridworksCore/Types/TypeInit/DeriveTypeInit.xslt b/CodeGenerationTools/GridworksCore/Types/TypeInit/DeriveTypeInit.xslt index 44693665..7c2e6094 100644 --- a/CodeGenerationTools/GridworksCore/Types/TypeInit/DeriveTypeInit.xslt +++ b/CodeGenerationTools/GridworksCore/Types/TypeInit/DeriveTypeInit.xslt @@ -24,13 +24,14 @@ Always - -""" List of all the types """ +""" List of all the types """ - + - - @@ -48,56 +49,45 @@ from gwproto.types. import - -from gwproto.types. - - import -_Maker - + __all__ = [ - + + + - - - + + - - + + - + + - + " - - - - # " - ", - - - " - - - - # " - - - _Maker", - + + + "cacs", # noqa: F822 + "components", # noqa: F822 ] diff --git a/CodeGenerationTools/GridworksCore/Types/TypeTests/DeriveTypeTests.xslt b/CodeGenerationTools/GridworksCore/Types/TypeTests/DeriveTypeTests.xslt index 9b9775bd..bdb42395 100644 --- a/CodeGenerationTools/GridworksCore/Types/TypeTests/DeriveTypeTests.xslt +++ b/CodeGenerationTools/GridworksCore/Types/TypeTests/DeriveTypeTests.xslt @@ -24,9 +24,17 @@ + + + + + + + + @@ -47,34 +55,40 @@ """Tests type, version """ -import json + + + + + + + + + + + + + +from gwproto.enums import + + + -import pytest -from pydantic import ValidationError + -from gwproto.errors import SchemaError from gwproto.types. - import -_Maker as Maker + import -from gwproto.types import -_Maker as Maker +from gwproto.types import - - -from gwproto.enums import - - - - + @@ -102,9 +116,9 @@ def test_ " - GtEnumSymbol": - - , + ": " + + ", @@ -115,256 +129,42 @@ def test_ "Version": "", } - with pytest.raises(SchemaError): - Maker.type_to_tuple(d) + d2 = .model_validate(d).model_dump(exclude_none=True) - with pytest.raises(SchemaError): - Maker.type_to_tuple('"not a dict"') - - # Test type_to_tuple - gtype = json.dumps(d) - gtuple = Maker.type_to_tuple(gtype) - - # test type_to_tuple and tuple_to_type maps - assert Maker.type_to_tuple(Maker.tuple_to_type(gtuple)) == gtuple - - # test Maker init - t = Maker( - - - - - - - - Id - - + assert d2 == d - - - - =gtuple. - - , - - - - ).tuple - assert t == gtuple - - - - ###################################### - # Dataclass related tests - ###################################### - - dc = Maker.tuple_to_dc(gtuple) - assert gtuple == Maker.dc_to_tuple(dc) - assert Maker.type_to_dc(Maker.dc_to_type(dc)) == dc - - - - ###################################### - # SchemaError raised if missing a required attribute - ###################################### - - d2 = dict(d) - del d2["TypeName"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - - - - - - - - - d2 = dict(d) - del d2[" - - - Id - - - "] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - - - - - - d2 = dict(d) - del d2[" - GtEnumSymbol"] - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - - - - - + + - - ###################################### - # Optional attributes can be removed from type ###################################### - - - - - - - d2 = dict(d) - if " - Id" in d2.keys(): - del d2[" - Id"] - Maker.dict_to_tuple(d2) - - - - - - d2 = dict(d) - if " - " in d2.keys(): - del d2[" - "] - Maker.dict_to_tuple(d2) - - - - - - ###################################### - # Behavior on incorrect types + # Enum related ###################################### - + - - - - - d2 = dict(d, - =" - .1") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - - - d2 = dict(d, - ="this is not a float") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - - - d2 = dict(d, - ="this is not a boolean") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - - - + assert type(d2[" + "]) is str d2 = dict(d, - GtEnumSymbol="unknown_symbol") - Maker.dict_to_tuple(d2). - + ="unknown_enum_thing") + assert + + (**d2). + == .default() - - - - - - - d2 = dict(d, - ="Not a list.") - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - d2 = dict(d, - =["Not a list of dicts"]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - d2 = dict(d, - = [{"Failed": "Not a GtSimpleSingleStatus"}]) - with pytest.raises(SchemaError): - Maker.dict_to_tuple(d2) - - - - - ###################################### - # SchemaError raised if TypeName is incorrect - ###################################### - - d2 = dict(d, TypeName="not the type name") - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - - ###################################### - # SchemaError raised if primitive attributes do not have appropriate property_format - ###################################### - - - - - - - - d2 = dict(d, - - = - ) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - d2 = dict(d, - - =[ - ]) - with pytest.raises(ValidationError): - Maker.dict_to_tuple(d2) - - - - - - diff --git a/CodeGenerationTools/GridworksCore/clean.sh b/CodeGenerationTools/GridworksCore/clean.sh index 1a257429..e1606fa2 100755 --- a/CodeGenerationTools/GridworksCore/clean.sh +++ b/CodeGenerationTools/GridworksCore/clean.sh @@ -1,14 +1,7 @@ -black ../../src/gwproto/enums/ -isort ../../src/gwproto/enums/ - -black ../../src/gwproto/types/ -isort ../../src/gwproto/types/ - -black ../../tests/enums/ -isort ../../tests/enums/ - -black ../../tests/types/ -isort ../../tests/types/ - -black ../../src/gwproto/types/__init__.py -black ../../src/gwproto/enums/__init__.py +git checkout -- ODXML SSoT aicapture.json +pushd ../.. +# TODO: There are times where we do want to generate docs +git checkout -- docs +poetry run ruff check +poetry run pre-commit run -a +popd From 529075c49ca61e6be76be8c004c6f2ad7f8d93b7 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:31:11 -0400 Subject: [PATCH 117/168] add CACS_BY_MAKE_MODEL, also ... Move WebServerGt (which does not have TypeName) from types init to type_helpers init Uncomment out various components as there are no longer problematic circular references with data classes --- src/gwproto/type_helpers/__init__.py | 4 ++ .../type_helpers/cacs_by_make_model.py | 40 +++++++++++++++++++ src/gwproto/types/__init__.py | 8 ++-- 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/gwproto/type_helpers/cacs_by_make_model.py diff --git a/src/gwproto/type_helpers/__init__.py b/src/gwproto/type_helpers/__init__.py index 75317637..b74bfb3e 100644 --- a/src/gwproto/type_helpers/__init__.py +++ b/src/gwproto/type_helpers/__init__.py @@ -1,3 +1,4 @@ +from gwproto.type_helpers.cacs_by_make_model import CACS_BY_MAKE_MODEL from gwproto.types.hubitat_component_gt import HubitatRESTResolutionSettings from gwproto.types.hubitat_poller_gt import HubitatPollerGt, MakerAPIAttributeGt from gwproto.types.hubitat_tank_gt import ( @@ -13,8 +14,10 @@ URLArgs, URLConfig, ) +from gwproto.types.web_server_gt import WebServerGt __all__ = [ + "CACS_BY_MAKE_MODEL", "AioHttpClientTimeout", "FibaroTempSensorSettings", "FibaroTempSensorSettingsGt", @@ -27,4 +30,5 @@ "SessionArgs", "URLArgs", "URLConfig", + "WebServerGt", ] diff --git a/src/gwproto/type_helpers/cacs_by_make_model.py b/src/gwproto/type_helpers/cacs_by_make_model.py new file mode 100644 index 00000000..21cc1f39 --- /dev/null +++ b/src/gwproto/type_helpers/cacs_by_make_model.py @@ -0,0 +1,40 @@ +from typing import Dict + +from gwproto.enums import MakeModel + +CACS_BY_MAKE_MODEL: Dict[MakeModel, str] = { + MakeModel.EGAUGE__4030: "739a6e32-bb9c-43bc-a28d-fb61be665522", + MakeModel.NCD__PR814SPST: "c6e736d8-8078-44f5-98bb-d72ca91dc773", + MakeModel.ADAFRUIT__642: "43564cd2-0e78-41a2-8b67-ad80c02161e8", + MakeModel.GRIDWORKS__WATERTEMPHIGHPRECISION: "7937eb7e-24d5-4d52-990f-cca063484df9", + MakeModel.GRIDWORKS__SIMPM1: "28897ac1-ea42-4633-96d3-196f63f5a951", + MakeModel.SCHNEIDERELECTRIC__IEM3455: "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", + MakeModel.GRIDWORKS__SIMBOOL30AMPRELAY: "69f101fc-22e4-4caa-8103-50b8aeb66028", + MakeModel.OPENENERGY__EMONPI: "357b9b4f-2550-4380-aa6b-d2cd9c7ba0f9", + MakeModel.GRIDWORKS__SIMTSNAP1: "b9f7135e-07a9-42f8-b847-a9bb3ea3770a", + MakeModel.ATLAS__EZFLO: "13d916dc-8764-4b16-b85d-b8ead3e2fc80", + MakeModel.HUBITAT__C7__LAN1: "62528da5-b510-4ac2-82c1-3782842eae07", + MakeModel.GRIDWORKS__TANK_MODULE_1: "60ac199d-679a-49f7-9142-8ca3e6428a5f", + MakeModel.FIBARO__ANALOG_TEMP_SENSOR: "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", + MakeModel.AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN: "2821c81d-054d-4003-9b07-2c295aef40f5", + MakeModel.YHDC__SCT013100: "812761ba-6544-4796-9aad-e1c979f58734", + MakeModel.MAGNELAB__SCT0300050: "cf312bd6-7ca5-403b-a61b-b2e817ea1e22", + MakeModel.GRIDWORKS__MULTITEMP1: "432073b8-4d2b-4e36-9229-73893f33f846", + MakeModel.KRIDA__EMR16I2CV3: "018d9ffb-89d1-4cc4-95c0-f170711b5ffa", + MakeModel.OMEGA__FTB8007HWPT: "8cf6c726-e38a-4900-9cfe-ae6f053aafdf", + MakeModel.ISTEC_4440: "62ed724c-ba62-4302-ae30-d52b20d42ad9", + MakeModel.OMEGA__FTB8010HWPT: "d9f225f8-eeb5-4cb7-b314-5551b925ea27", + MakeModel.BELIMO__BALLVALVE232VS: "a2236d8c-7c9b-403f-9c55-733c62971d09", + MakeModel.BELIMO__DIVERTERB332L: "f3261ed0-3fb1-4def-b60b-246960bf85ef", + MakeModel.TACO__0034EPLUS: "3880ba73-61e5-4b35-9df1-e154a03a3335", + MakeModel.TACO__007E: "198ebac8-e0b9-4cee-ae91-2ee6db708491", + MakeModel.ARMSTRONG__COMPASSH: "ff6863e1-d5f7-4066-8579-2768162321a6", + MakeModel.HONEYWELL__T6ZWAVETHERMOSTAT: "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", + MakeModel.PRMFILTRATION__WM075: "61d5c12d-eeca-4835-9a11-e61167d82e0d", + MakeModel.BELLGOSSETT__ECOCIRC20_18: "0d2ccc36-d2b8-405d-a257-3917111607c5", + MakeModel.TEWA__TT0P10KC3T1051500: "20779dbb-0302-4c36-9d60-e1962857c2f3", + MakeModel.EKM__HOTSPWM075HD: "e52cb571-913a-4614-90f4-5cc81f8e7fe5", + MakeModel.GRIDWORKS__SIMMULTITEMP: "627ac482-24fe-46b2-ba8c-3d6f1e1ee069", + MakeModel.GRIDWORKS__SIMTOTALIZER: "a88f8f4c-fe1e-4645-a7f4-249912131dc8", + MakeModel.KRIDA__DOUBLEEMR16I2CV3: "29eab8b1-100f-4230-bb44-3a2fcba33cc3", +} diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 4262665e..e5936fa8 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -10,6 +10,7 @@ from gwproto.types.electric_meter_cac_gt import ( ElectricMeterCacGt, ) +from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt from gwproto.types.fibaro_smart_implant_cac_gt import ( FibaroSmartImplantCacGt, ) @@ -56,6 +57,7 @@ from gwproto.types.multipurpose_sensor_cac_gt import ( MultipurposeSensorCacGt, ) +from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt from gwproto.types.pipe_flow_sensor_cac_gt import ( PipeFlowSensorCacGt, ) @@ -90,7 +92,6 @@ from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, ) -from gwproto.types.web_server_gt import WebServerGt __all__ = [ "ComponentAttributeClassGt", @@ -99,7 +100,7 @@ "EgaugeIo", "EgaugeRegisterConfig", "ElectricMeterCacGt", - # "ElectricMeterComponentGt", + "ElectricMeterComponentGt", "FibaroSmartImplantCacGt", "FibaroSmartImplantComponentGt", "GtDispatchBoolean", @@ -120,7 +121,7 @@ "HubitatTankCacGt", "HubitatTankComponentGt", "MultipurposeSensorCacGt", - # "MultipurposeSensorComponentGt", + "MultipurposeSensorComponentGt", "PipeFlowSensorCacGt", "PipeFlowSensorComponentGt", "PowerWatts", @@ -137,7 +138,6 @@ "TaDataChannels", "TelemetryReportingConfig", "TelemetrySnapshotSpaceheat", - "WebServerGt", "cacs", # noqa: F822 "components", # noqa: F822 ] From a6676600a0748981515913675fb37bae1d99c177 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:32:50 -0400 Subject: [PATCH 118/168] Update enums to use gw.GwStrEnum --- src/gwproto/enums/actor_class.py | 172 +++++----------- src/gwproto/enums/local_comm_interface.py | 71 +------ src/gwproto/enums/make_model.py | 234 +++++++++------------- src/gwproto/enums/role.py | 147 +------------- src/gwproto/enums/symbolized.py | 11 - src/gwproto/enums/telemetry_name.py | 143 +++---------- src/gwproto/enums/unit.py | 132 +++--------- tests/enums/actor_class_test.py | 18 +- tests/enums/local_comm_interface_test.py | 37 ---- tests/enums/make_model_test.py | 42 +++- tests/enums/role_test.py | 59 ------ tests/enums/telemetry_name_test.py | 5 +- tests/enums/unit_test.py | 9 +- 13 files changed, 260 insertions(+), 820 deletions(-) delete mode 100644 src/gwproto/enums/symbolized.py delete mode 100644 tests/enums/local_comm_interface_test.py delete mode 100644 tests/enums/role_test.py diff --git a/src/gwproto/enums/actor_class.py b/src/gwproto/enums/actor_class.py index 2cedbe62..eea00bc5 100644 --- a/src/gwproto/enums/actor_class.py +++ b/src/gwproto/enums/actor_class.py @@ -1,63 +1,78 @@ -# ruff: noqa: RUF100 from enum import auto -from typing import List +from typing import Optional -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class ActorClass(SymbolizedEnum): +class ActorClass(GwStrEnum): """ Determines the code running Spaceheat Nodes supervised by Spaceheat SCADA software Enum sh.actor.class version 001 in the GridWorks Type registry. - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: + Used by multiple Application Shared Languages (ASLs). For more information: - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shactorclass) - [More Info](https://gridworks-protocol.readthedocs.io/en/latest/actor-class.html) - Values (with symbols in parens): - - NoActor (00000000): A SpaceheatNode that does not have any code running on its behalf within + Values: + - NoActor: A SpaceheatNode that does not have any code running on its behalf within the SCADA, but is instead only a reference object (for example, a tank of hot water or a resistive element) that can be discussed (for example, the power drawn by the resistive element can be measured) or evaluated (for example, a set of 5 different temperatures in different places on the tank can be used to estimate total thermal energy in the tank). - - Scada (6d37aa41): The SCADA actor is the prime piece of code running and supervising other + - Scada: The SCADA actor is the prime piece of code running and supervising other ProActors within the SCADA code. It is also responsible for managing the state of TalkingWith the AtomicTNode, as well maintaining and reporting a boolean state variable that indicates whether it is following dispatch commands from the AtomicTNode XOR following dispatch commands from its own HomeAlone actor. - - HomeAlone (32d3d19f): HomeAlone is an abstract Spaceheat Actor responsible for dispatching + - HomeAlone: HomeAlone is an abstract Spaceheat Actor responsible for dispatching the SCADA when it is not talking with the AtomicTNode. - - BooleanActuator (fddd0064): A SpaceheatNode representing a generic boolean actuator capable - of turning on (closing a circuit) or turning off (opening a circuit). - - PowerMeter (2ea112b9): A SpaceheatNode representing the power meter that is used to settle + - BooleanActuator: A SpaceheatNode representing a generic boolean actuator capable + of turning on (closing a circuit) or turning off (opening a circuit). If the device + is a relay that can be directly energized or de-energized, recommend using Relay actor + instead. + - PowerMeter: A SpaceheatNode representing the power meter that is used to settle financial transactions with the TerminalAsset. That is, this is the power meter whose accuracy is certified in the creation of the TerminalAsset GNode via creation of the TaDeed. [More Info](https://gridworks.readthedocs.io/en/latest/terminal-asset.html). - - Atn (b103058f): A SpaceheatNode representing the AtomicTNode. Note that the code running + - Atn: A SpaceheatNode representing the AtomicTNode. Note that the code running the AtomicTNode is not local within the SCADA code, except for a stub used for testing purposes. [More Info](https://gridworks.readthedocs.io/en/latest/atomic-t-node.html). - - SimpleSensor (dae4b2f0): A SpaceheatNode representing a sensor that measures a single category + - SimpleSensor: A SpaceheatNode representing a sensor that measures a single category of quantity (for example, temperature) for a single object (for example, on a pipe). [More Info](https://gridworks-protocol.readthedocs.io/en/latest/simple-sensor.html). - - MultipurposeSensor (7c483ad0): A sensor that either reads multiple kinds of readings from + - MultipurposeSensor: A sensor that either reads multiple kinds of readings from the same sensing device (for example reads current and voltage), reads multiple different objects (temperature from two different thermisters) or both. [More Info](https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html). - - Thermostat (4a9c1785): A SpaceheatNode representing a thermostat. - - HubitatTelemetryReader (0401b27e): A generic actor for reading telemetry data from a Hubitat + - Thermostat: A SpaceheatNode representing a thermostat. + - HubitatTelemetryReader: A generic actor for reading telemetry data from a Hubitat Home Automation Hub LAN API. [More Info](https://drive.google.com/drive/u/0/folders/1AqAU_lC2phzuI9XRYvogiIYA7GXNtlr6). - - HubitatTankModule (e2877329): The actor for running a GridWorks TankModule, comprised of + - HubitatTankModule: The actor for running a GridWorks TankModule, comprised of two Z-Wave Fibaro temp sensors built together inside a small container that has 4 thermistors attached. These are designed to be installed from top (1) to bottom (4) on a stratified thermal storage tank. [More Info](https://drive.google.com/drive/u/0/folders/1GSxDd8Naf1GKK_fSOgQU933M1UcJ4r8q). - - HubitatPoller (00000100): An actor for representing a somewhat generic ShNode (like a thermostat) + - HubitatPoller: An actor for representing a somewhat generic ShNode (like a thermostat) that can be polled through the Hubitat. - - Hubitat: (0000101): An actor for representing a Hubitat for receiving Hubitat events over HTTP. - - HoneywellThermostat: (0000102): An actor for representing a Honeywell Hubitat thermostat which - can load thermostat heating state change messages into status reports. - + - I2cRelayMultiplexer: Responsible for maintaining a single i2c bus object + - FlowTotalizer: Attached to a driver that reads liquid flow by counting pulses + from a flow meter that creates pulses and integrating the result (known as a totalizer + in the industry). + - Relay: An actor representing a relay. If the device is indeed relay that can be + directly energized or de-energized, recommend using Relay instead of BooleanActuator + - Admin: Actor for taking control of all of the actuators - flattening the hierarchy + and disabling all finite state machines. + - Fsm: Actor Class for Finite State Machine actors. For these actors, the code is + determined by the ShNode Name instead of just the ActorClass. + - Parentless: An actor that has no parent and is also not the primary SCADA. Used + when there are multiple devices in the SCADA's system. For example, two Pis - one running + the primary SCADA code and temp sensors, the other running relays and 0-10V output devices. + A Parentless actor on the second Pi is responsible for spinning up the relay- and 0-10V + output actors. + - Hubitat: An actor for representing a Hubitat for receiving Hubitat events over + HTTP. + - HoneywellThermostat: An actor for representing a Honeywell Hubitat thermostat + which can load thermostat heating state change messages into status reports. """ NoActor = auto() @@ -72,6 +87,12 @@ class ActorClass(SymbolizedEnum): HubitatTelemetryReader = auto() HubitatTankModule = auto() HubitatPoller = auto() + I2cRelayMultiplexer = auto() + FlowTotalizer = auto() + Relay = auto() + Admin = auto() + Fsm = auto() + Parentless = auto() Hubitat = auto() HoneywellThermostat = auto() @@ -83,24 +104,11 @@ def default(cls) -> "ActorClass": return cls.NoActor @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "001" if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") # noqa: TRY004 + raise TypeError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -119,82 +127,6 @@ def enum_version(cls) -> str: """ return "001" - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "NoActor". - """ - if symbol not in symbol_to_value: - return str(cls.default().value) - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a ActorClass enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "6d37aa41", - "32d3d19f", - "fddd0064", - "2ea112b9", - "b103058f", - "dae4b2f0", - "7c483ad0", - "4a9c1785", - "0401b27e", - "e2877329", - "00000100", - "00000101", - "00000102", - ] - - -symbol_to_value = { - "00000000": "NoActor", - "6d37aa41": "Scada", - "32d3d19f": "HomeAlone", - "fddd0064": "BooleanActuator", - "2ea112b9": "PowerMeter", - "b103058f": "Atn", - "dae4b2f0": "SimpleSensor", - "7c483ad0": "MultipurposeSensor", - "4a9c1785": "Thermostat", - "0401b27e": "HubitatTelemetryReader", - "e2877329": "HubitatTankModule", - "00000100": "HubitatPoller", - "00000101": "Hubitat", - "00000102": "HoneywellThermostat", -} - -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { "NoActor": "000", @@ -209,6 +141,12 @@ def symbols(cls) -> List[str]: "HubitatTelemetryReader": "001", "HubitatTankModule": "001", "HubitatPoller": "001", + "I2cRelayMultiplexer": "001", + "FlowTotalizer": "001", + "Relay": "001", + "Admin": "001", + "Fsm": "001", + "Parentless": "001", "Hubitat": "001", "HoneywellThermostat": "001", } diff --git a/src/gwproto/enums/local_comm_interface.py b/src/gwproto/enums/local_comm_interface.py index 5f42ce73..654ff77c 100644 --- a/src/gwproto/enums/local_comm_interface.py +++ b/src/gwproto/enums/local_comm_interface.py @@ -1,10 +1,9 @@ from enum import auto -from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class LocalCommInterface(SymbolizedEnum): +class LocalCommInterface(GwStrEnum): """ Categorization of in-house comm mechanisms for SCADA @@ -81,72 +80,6 @@ def enum_version(cls) -> str: """ return "000" - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "Unknown". - """ - if symbol not in symbol_to_value: - return str(cls.default().value) - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a LocalCommInterface enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[str(cls.default().value)] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "9ec8bc49", - "c1e7a955", - "ae2d4cd8", - "a6a4ac9f", - "efc144cd", - "46ac6589", - "653c73b8", - "0843a726", - ] - - -symbol_to_value = { - "00000000": "UNKNOWN", - "9ec8bc49": "I2C", - "c1e7a955": "ETHERNET", - "ae2d4cd8": "ONEWIRE", - "a6a4ac9f": "RS485", - "efc144cd": "SIMRABBIT", - "46ac6589": "WIFI", - "653c73b8": "ANALOG_4_20_MA", - "0843a726": "RS232", -} - -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { "UNKNOWN": "000", diff --git a/src/gwproto/enums/make_model.py b/src/gwproto/enums/make_model.py index ecaf7401..72b15ff8 100644 --- a/src/gwproto/enums/make_model.py +++ b/src/gwproto/enums/make_model.py @@ -1,48 +1,48 @@ from enum import auto -from typing import List +from typing import Optional -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class MakeModel(SymbolizedEnum): +class MakeModel(GwStrEnum): """ Determines Make/Model of device associated to a Spaceheat Node supervised by SCADA - Enum spaceheat.make.model version 001 in the GridWorks Type registry. + Enum spaceheat.make.model version 002 in the GridWorks Type registry. - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: + Used by multiple Application Shared Languages (ASLs). For more information: - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel) - [More Info](https://gridworks-protocol.readthedocs.io/en/latest/make-model.html) - Values (with symbols in parens): - - UnknownMake__UnknownModel (00000000) - - Egauge__4030 (beb6d3fb): A power meter in Egauge's 403x line. [More Info](https://drive.google.com/drive/u/0/folders/1abJ-o9tlTscsQpMvT6SHxIm5j5aODgfA). - - NCD__PR8-14-SPST (fabfa505): NCD's 4-channel high-power relay controller + 4 GPIO with I2C + Values: + - UnknownMake__UnknownModel + - Egauge__4030: A power meter in Egauge's 403x line. [More Info](https://drive.google.com/drive/u/0/folders/1abJ-o9tlTscsQpMvT6SHxIm5j5aODgfA). + - NCD__PR8-14-SPST: NCD's 4-channel high-power relay controller + 4 GPIO with I2C interface. [More Info](https://store.ncd.io/product/4-channel-high-power-relay-controller-4-gpio-with-i2c-interface/?attribute_pa_choose-a-relay=20-amp-spdt). - - Adafruit__642 (acd93fb3): Adafruit's high-temp, water-proof 1-wire temp sensor. [More Info](https://www.adafruit.com/product/642). - - GridWorks__TSnap1 (d0178dc3): Actual GridWorks TSnap 1.0 SCADA Box. - - GridWorks__WaterTempHighPrecision (f8b497e8): Simulated temp sensor. - - Gridworks__SimPm1 (076da322): Simulated power meter. - - SchneiderElectric__Iem3455 (d300635e): Schneider Electric IEM 344 utility meter. - - GridWorks__SimBool30AmpRelay (e81d74a8): Simulated relay. - - OpenEnergy__EmonPi (c75d269f): Open Energy's open source multipurpose sensing device (including + - Adafruit__642: Adafruit's high-temp, water-proof 1-wire temp sensor. [More Info](https://www.adafruit.com/product/642). + - GridWorks__TSnap1: Actual GridWorks TSnap 1.0 SCADA Box. + - GridWorks__WaterTempHighPrecision: PlaceHolder for some new GridWorks designed + device. + - Gridworks__SimPm1: Simulated power meter. + - SchneiderElectric__Iem3455: Schneider Electric IEM 344 utility meter. + - GridWorks__SimBool30AmpRelay: Simulated relay. + - OpenEnergy__EmonPi: Open Energy's open source multipurpose sensing device (including internal power meter). [More Info](https://docs.openenergymonitor.org/emonpi/technical.html). - - GridWorks__SimTSnap1 (3042c432): Simulated SCADA Box. - - Atlas__EzFlo (d0b0e375): Atlas Scientific EZO Embedded Flow Meter Totalizer, pulse to I2C. [More Info](https://drive.google.com/drive/u/0/folders/142bBV1pQIbMpyIR_0iRUr5gnzWgknOJp). - - Hubitat__C7__LAN1 (4d649420): This refers to a Hubitat C7 that has been configured in a specific + - GridWorks__SimTSnap1: Simulated SCADA Box. + - Atlas__EzFlo: Atlas Scientific EZO Embedded Flow Meter Totalizer, pulse to I2C. [More Info](https://drive.google.com/drive/u/0/folders/142bBV1pQIbMpyIR_0iRUr5gnzWgknOJp). + - Hubitat__C7__LAN1: This refers to a Hubitat C7 that has been configured in a specific way with respect to the APIs it presents on the Local Area Network. The Hubitat C7 is a home automation hub that supports building ZigBee and ZWave meshes, plugs into Ethernet, has a reasonable user interface and has an active community of open-source developers who create drivers and package managers for devices, and supports the creation of various types of APIs on the Local Area Network. [More Info](https://drive.google.com/drive/folders/1AqAU_lC2phzuI9XRYvogiIYA7GXNtlr6). - - GridWorks__Tank_Module_1 (bd759051): This refers to a small module designed and assembled + - GridWorks__Tank_Module_1: This refers to a small module designed and assembled by GridWorks that is meant to be mounted to the side of a hot water tank. It requires 24V DC and has 4 temperature sensors coming out of it labeled 1, 2, 3 and 4. It is meant to provide temperature readings (taken within a half a second of each other) of all 4 of its sensors once a minute. [More Info](https://drive.google.com/drive/folders/1GSxDd8Naf1GKK_fSOgQU933M1UcJ4r8q). - - Fibaro__Analog_Temp_Sensor (1f19839d): This enum refers to a Fibaro FGBS-222 home automation + - Fibaro__Analog_Temp_Sensor: This enum refers to a Fibaro FGBS-222 home automation device that has been configured in a specific way. This includes (1) being attached to two 10K NTC thermistors and a specific voltage divider circuit that specifies its temperature as a function of voltage and (2) one of its potential free outputs being @@ -51,14 +51,43 @@ class MakeModel(SymbolizedEnum): power cylced). The Fibaro itself is a tiny (29 X 18 X 13 mm) Z-Wave device powered on 9-30V DC that can read up to 6 1-wire DS18B20 temp sensors, 2 0-10V analog inputs and also has 2 potential free outputs. [More Info](https://drive.google.com/drive/u/0/folders/1Muhsvw00goppHIfGSEmreX4hM6V78b-m). - - Amphenol__NTC_10K_Thermistor_MA100GG103BN (46f21cd5): A small gauge, low-cost, rapid response + - Amphenol__NTC_10K_Thermistor_MA100GG103BN: A small gauge, low-cost, rapid response NTC 10K Thermistor designed for medical applications. [More Info](https://drive.google.com/drive/u/0/folders/11HW4ov66UvxKAwqApW6IrtoXatZBLQkd). - - YHDC__SCT013-100 (08da3f7d): YHDC current transformer [More Info](https://en.yhdc.com/product/SCT013-401.html). - - Magnelab__SCT-0300-050 (a8d9a70d): Magnelab 50A current transformer - - GridWorks__MultiTemp1 (bb31d136): GridWorks Analog temperature sensor that has 12 channels - (labeled 1-12) to read 12 10K NTC Thermistors. It is comprised of 3 NCD ADS 1115 I2C - temperature sensors with I2C Addresses 0x4b, 0x48, 0x49. [More Info](https://drive.google.com/drive/u/0/folders/1OuY0tunaad2Ie4Id3zFB7FcbEwHizWuL). - - Krida__Emr16-I2c-V3 (3353ce46): 16-Channel I2C Low Voltage Electromagnetic Relay Board [More Info](https://drive.google.com/drive/u/0/folders/1jL82MTRKEh9DDmxJFQ2yU2cjqnVD9Ik7). + - YHDC__SCT013-100: YHDC current transformer. [More Info](https://en.yhdc.com/product/SCT013-401.html). + - Magnelab__SCT-0300-050: Magnelab 50A current transformer. + - GridWorks__MultiTemp1: GridWorks ADS 1115-based analog temperature sensor that + has 12 channels (labeled 1-12) to read 12 10K NTC Thermistors. It is comprised of 3 + NCD ADS 1115 I2C temperature sensors with I2C Addresses 0x4b, 0x48, 0x49. [More Info](https://drive.google.com/drive/u/0/folders/1OuY0tunaad2Ie4Id3zFB7FcbEwHizWuL). + - Krida__Emr16-I2c-V3: 16-Channel I2C Low Voltage Electromagnetic Relay Board. [More Info](https://drive.google.com/drive/u/0/folders/1jL82MTRKEh9DDmxJFQ2yU2cjqnVD9Ik7). + - Omega__FTB8007HW-PT: A double-jet reed pulse producing Flow Meter with 3/4" pipe, + one pulse per 1/10th of a gallon. [More Info](https://drive.google.com/drive/u/0/folders/1gPR4nIGUuEVyBqBjb2wfY1Znqh6MvKWw). + - Istec_4440: A double-jet reed pulse producing Flow Meter with 3/4" pipe, somewhat + strange pulse output. [More Info](https://drive.google.com/drive/u/0/folders/1nioNO_XeEzE4NQJKXvuFq74_HH1vwRc6). + - Omega__FTB8010HW-PT: A double-jet reed pulse producingFlow Meter with 1" pipe, + one pulse per gallon. Rated for water to 195F. [More Info](https://drive.google.com/drive/u/0/folders/1fiFr9hwYGeXZ1SmpxaSz_XROhfThGbq8). + - Belimo__BallValve232VS: Belimo Ball Valve. Configurable to be either normally + open or normally closed. Goes into its powered position over about a minute and winds + up a spring as it does that. Moves back to un-powered position in about 20 seconds, [More Info](https://drive.google.com/drive/u/0/folders/1eTqPNKaKzjKSWwnvY36tZkkv4WVdvrR3). + - Belimo__DiverterB332L: Belimo 3-way diverter valve, 1.25", 24 VAC, spring return + actuator. [More Info](https://drive.google.com/drive/u/0/folders/1YF_JdUoXrT3bDoXvEwqEvAi7EjahErHk). + - Taco__0034ePLUS: Taco 0034ePLUS 010V controllable pump. [More Info](https://drive.google.com/drive/u/0/folders/1GUaQnrfiJeAmmfMiZT1fjPPIXxcTtTsj). + - Taco__007e: Taco 007e basic circulator pump. [More Info](https://drive.google.com/drive/u/0/folders/12LIMxHMFXujV7mY53IItKP3J2EaM2JlV). + - Armstrong__CompassH: Armstrong CompassH 010V controllable pump. [More Info](https://drive.google.com/drive/u/0/folders/1lpdvjVYD9qk7AHQnRSoY9Xf_o_L0tY38). + - Honeywell__T6-ZWave-Thermostat: Honeywell TH6320ZW2003 T6 Pro Series Z-Wave Thermostat. [More Info](https://drive.google.com/drive/u/0/folders/1mqnU95tOdeeSGA6o3Ac_sJ1juDy84BIE). + - PRMFiltration__WM075: A double-jet reed pulse producing Flow Meter with 3/4" pipe, + one pulse per gallon. Cheaper than omegas. [More Info](https://drive.google.com/drive/u/0/folders/1LW-8GHekH9I8vUtT7_xC_9KvkwfZBvid). + - BellGossett__Ecocirc20_18: A 0-10V controllable pump that switches out of 0-10V + control when sent a 0 V signal. + - Tewa__TT0P-10KC3-T105-1500: A 10K NTC thermistor used for wrapping around water + pipes. [More Info](https://drive.google.com/drive/u/0/folders/1lZFZbpjBFgAQ_wlnKJxmEeiN-EOV9Erl). + - EKM__HOT-SPWM-075-HD: 3/4" horizontal hot water flow pulse meter, 1 pulse per + 1/100 cubic ft (~0.0748 gallons). + - GridWorks__SimMultiTemp: Simulated 12-channel Ads111x-based analog temp sensor + - GridWorks__SimTotalizer: Simulated I2c-based pulse counter. + - Krida__Double-Emr16-I2c-V3: Two 16-Channel I2C Low Voltage Electromagnetic Relay + Board, with first at address 0x20 and second at address 0x21 + - GridWorks__SimDouble16PinI2cRelay: Simulated I2c Relay board with two boards and + 32 pins (for dev code using Krida__Doubler-Emr16-I2c-V3). """ UNKNOWNMAKE__UNKNOWNMODEL = auto() @@ -81,6 +110,23 @@ class MakeModel(SymbolizedEnum): MAGNELAB__SCT0300050 = auto() GRIDWORKS__MULTITEMP1 = auto() KRIDA__EMR16I2CV3 = auto() + OMEGA__FTB8007HWPT = auto() + ISTEC_4440 = auto() + OMEGA__FTB8010HWPT = auto() + BELIMO__BALLVALVE232VS = auto() + BELIMO__DIVERTERB332L = auto() + TACO__0034EPLUS = auto() + TACO__007E = auto() + ARMSTRONG__COMPASSH = auto() + HONEYWELL__T6ZWAVETHERMOSTAT = auto() + PRMFILTRATION__WM075 = auto() + BELLGOSSETT__ECOCIRC20_18 = auto() + TEWA__TT0P10KC3T1051500 = auto() + EKM__HOTSPWM075HD = auto() + GRIDWORKS__SIMMULTITEMP = auto() + GRIDWORKS__SIMTOTALIZER = auto() + KRIDA__DOUBLEEMR16I2CV3 = auto() + GRIDWORKS__SIMDOUBLE16PINI2CRELAY = auto() @classmethod def default(cls) -> "MakeModel": @@ -90,24 +136,11 @@ def default(cls) -> "MakeModel": return cls.UNKNOWNMAKE__UNKNOWNMODEL @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "002" if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") # noqa: TRY004 + raise TypeError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -122,98 +155,10 @@ def enum_name(cls) -> str: @classmethod def enum_version(cls) -> str: """ - The version in the GridWorks Type Registry (001) + The version in the GridWorks Type Registry (002) """ - return "001" - - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "UnknownMake__UnknownModel". - """ - if symbol not in symbol_to_value: - return cls.default().value # noqa: ALL - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a MakeModel enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "beb6d3fb", - "fabfa505", - "acd93fb3", - "d0178dc3", - "f8b497e8", - "076da322", - "d300635e", - "e81d74a8", - "c75d269f", - "3042c432", - "d0b0e375", - "4d649420", - "bd759051", - "1f19839d", - "46f21cd5", - "08da3f7d", - "a8d9a70d", - "bb31d136", - "3353ce46", - ] - - -symbol_to_value = { - "00000000": "UNKNOWNMAKE__UNKNOWNMODEL", - "beb6d3fb": "EGAUGE__4030", - "fabfa505": "NCD__PR814SPST", - "acd93fb3": "ADAFRUIT__642", - "d0178dc3": "GRIDWORKS__TSNAP1", - "f8b497e8": "GRIDWORKS__WATERTEMPHIGHPRECISION", - "076da322": "GRIDWORKS__SIMPM1", - "d300635e": "SCHNEIDERELECTRIC__IEM3455", - "e81d74a8": "GRIDWORKS__SIMBOOL30AMPRELAY", - "c75d269f": "OPENENERGY__EMONPI", - "3042c432": "GRIDWORKS__SIMTSNAP1", - "d0b0e375": "ATLAS__EZFLO", - "4d649420": "HUBITAT__C7__LAN1", - "bd759051": "GRIDWORKS__TANK_MODULE_1", - "1f19839d": "FIBARO__ANALOG_TEMP_SENSOR", - "46f21cd5": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", - "08da3f7d": "YHDC__SCT013100", - "a8d9a70d": "MAGNELAB__SCT0300050", - "bb31d136": "GRIDWORKS__MULTITEMP1", - "3353ce46": "KRIDA__EMR16I2CV3", -} + return "002" -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { "UNKNOWNMAKE__UNKNOWNMODEL": "000", @@ -236,4 +181,21 @@ def symbols(cls) -> List[str]: "MAGNELAB__SCT0300050": "001", "GRIDWORKS__MULTITEMP1": "001", "KRIDA__EMR16I2CV3": "001", + "OMEGA__FTB8007HWPT": "002", + "ISTEC_4440": "002", + "OMEGA__FTB8010HWPT": "002", + "BELIMO__BALLVALVE232VS": "002", + "BELIMO__DIVERTERB332L": "002", + "TACO__0034EPLUS": "002", + "TACO__007E": "002", + "ARMSTRONG__COMPASSH": "002", + "HONEYWELL__T6ZWAVETHERMOSTAT": "002", + "PRMFILTRATION__WM075": "002", + "BELLGOSSETT__ECOCIRC20_18": "002", + "TEWA__TT0P10KC3T1051500": "002", + "EKM__HOTSPWM075HD": "002", + "GRIDWORKS__SIMMULTITEMP": "002", + "GRIDWORKS__SIMTOTALIZER": "002", + "KRIDA__DOUBLEEMR16I2CV3": "002", + "GRIDWORKS__SIMDOUBLE16PINI2CRELAY": "002", } diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py index d0644632..c2584ddb 100644 --- a/src/gwproto/enums/role.py +++ b/src/gwproto/enums/role.py @@ -1,10 +1,9 @@ from enum import auto -from typing import List -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class Role(SymbolizedEnum): +class Role(GwStrEnum): """ Categorizes SpaceheatNodes by their function within the heating system @@ -77,151 +76,9 @@ def default(cls) -> "Role": """ return cls.Unknown - @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ - if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") # noqa: TRY004 - if value not in value_to_version: - raise ValueError(f"Unknown enum value: {value}") - return value_to_version[value] - @classmethod def enum_name(cls) -> str: """ The name in the GridWorks Type Registry (sh.node.role) """ return "sh.node.role" - - @classmethod - def enum_version(cls) -> str: - """ - The version in the GridWorks Type Registry (000) - """ - return "000" - - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "Unknown". - """ - if symbol not in symbol_to_value: - return cls.default().value # noqa: ALL - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a Role enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "d0afb424", - "863e50d1", - "6ddff83b", - "9ac68b6e", - "99c5f326", - "57b788ee", - "3ecfe9b8", - "73308a1f", - "c480f612", - "fec74958", - "5938bf1f", - "ece3b600", - "65725f44", - "fe3cbdd5", - "05fdd645", - "6896109b", - "b0eaf2ba", - "661d7e73", - "dd975b31", - ] - - -symbol_to_value = { - "00000000": "Unknown", - "d0afb424": "Scada", - "863e50d1": "HomeAlone", - "6ddff83b": "Atn", - "9ac68b6e": "PowerMeter", - "99c5f326": "BoostElement", - "57b788ee": "BooleanActuator", - "3ecfe9b8": "DedicatedThermalStore", - "73308a1f": "TankWaterTempSensor", - "c480f612": "PipeTempSensor", - "fec74958": "RoomTempSensor", - "5938bf1f": "OutdoorTempSensor", - "ece3b600": "PipeFlowMeter", - "65725f44": "HeatedSpace", - "fe3cbdd5": "HydronicPipe", - "05fdd645": "BaseboardRadiator", - "6896109b": "RadiatorFan", - "b0eaf2ba": "CirculatorPump", - "661d7e73": "MultiChannelAnalogTempSensor", - "dd975b31": "Outdoors", -} - -value_to_symbol = {value: key for key, value in symbol_to_value.items()} - -value_to_version = { - "Unknown": "000", - "Scada": "000", - "HomeAlone": "000", - "Atn": "000", - "PowerMeter": "000", - "BoostElement": "000", - "BooleanActuator": "000", - "DedicatedThermalStore": "000", - "TankWaterTempSensor": "000", - "PipeTempSensor": "000", - "RoomTempSensor": "000", - "OutdoorTempSensor": "000", - "PipeFlowMeter": "000", - "HeatedSpace": "000", - "HydronicPipe": "000", - "BaseboardRadiator": "000", - "RadiatorFan": "000", - "CirculatorPump": "000", - "MultiChannelAnalogTempSensor": "000", - "Outdoors": "000", -} diff --git a/src/gwproto/enums/symbolized.py b/src/gwproto/enums/symbolized.py deleted file mode 100644 index f8490f44..00000000 --- a/src/gwproto/enums/symbolized.py +++ /dev/null @@ -1,11 +0,0 @@ -from gwproto.enums.better_str_enum import BetterStrEnum - - -class SymbolizedEnum(BetterStrEnum): - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - raise NotImplementedError - - @classmethod - def value_to_symbol(cls, value: str) -> str: - raise NotImplementedError diff --git a/src/gwproto/enums/telemetry_name.py b/src/gwproto/enums/telemetry_name.py index fd395993..bd43ebee 100644 --- a/src/gwproto/enums/telemetry_name.py +++ b/src/gwproto/enums/telemetry_name.py @@ -1,47 +1,43 @@ from enum import auto -from typing import List +from typing import Optional -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class TelemetryName(SymbolizedEnum): +class TelemetryName(GwStrEnum): """ Specifies the name of sensed data reported by a Spaceheat SCADA Enum spaceheat.telemetry.name version 001 in the GridWorks Type registry. - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: + Used by multiple Application Shared Languages (ASLs). For more information: - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname) - [More Info](https://gridworks-protocol.readthedocs.io/en/latest/telemetry-name.html) - Values (with symbols in parens): - - Unknown (00000000): Default Value - unknown telemetry name. - - PowerW (af39eec9): Power in Watts. - - RelayState (5a71d4b3): An associated read must be either 0 or 1, with 0 meaning that the - relay is open and current CANNOT flow and 1 meaning that the relay is closed and current - CAN flow. Note in particular that this TelemetryName is NOT meant to be used to reflect - whether a relay is energized or de-energized and in particular '1' means the same thing - for both Normally Open and Normally Closed relays. Also, it is not meant to be used - for a double-throw relay. - - WaterTempCTimes1000 (c89d0ba1): Water temperature, in Degrees Celcius multiplied by 1000. + Values: + - Unknown: Default Value - unknown telemetry name. + - PowerW: Power in Watts. + - RelayState: The Telemetry reading belongs to ['Energized', 'DeEnergized'] (relay.energization.state + enum). + - WaterTempCTimes1000: Water temperature, in Degrees Celcius multiplied by 1000. Example: 43200 means 43.2 deg Celcius. - - WaterTempFTimes1000 (793505aa): Water temperature, in Degrees F multiplied by 1000. Example: + - WaterTempFTimes1000: Water temperature, in Degrees F multiplied by 1000. Example: 142100 means 142.1 deg Fahrenheit. - - GpmTimes100 (d70cce28): Gallons Per Minute multiplied by 100. Example: 433 means 4.33 gallons + - GpmTimes100: Gallons Per Minute multiplied by 100. Example: 433 means 4.33 gallons per minute. - - CurrentRmsMicroAmps (ad19e79c): Current measurement in Root Mean Square MicroAmps. - - GallonsTimes100 (329a68c0): Gallons multipled by 100. This is useful for flow meters that + - CurrentRmsMicroAmps: Current measurement in Root Mean Square MicroAmps. + - GallonsTimes100: Gallons multipled by 100. This is useful for flow meters that report cumulative gallons as their raw output. Example: 55300 means 55.3 gallons. - - VoltageRmsMilliVolts (bb6fdd59): Voltage in Root Mean Square MilliVolts. - - MilliWattHours (e0bb014b): Energy in MilliWattHours. - - FrequencyMicroHz (337b8659): Frequency in MicroHz. Example: 59,965,332 means 59.965332 Hz. - - AirTempCTimes1000 (0f627faa): Air temperature, in Degrees Celsius multiplied by 1000. Example: + - VoltageRmsMilliVolts: Voltage in Root Mean Square MilliVolts. + - MilliWattHours: Energy in MilliWattHours. + - FrequencyMicroHz: Frequency in MicroHz. Example: 59,965,332 means 59.965332 Hz. + - AirTempCTimes1000: Air temperature, in Degrees Celsius multiplied by 1000. Example: 6234 means 6.234 deg Celcius. - - AirTempFTimes1000 (4c3f8c78): Air temperature, in Degrees F multiplied by 1000. Example: + - AirTempFTimes1000: Air temperature, in Degrees F multiplied by 1000. Example: 69329 means 69.329 deg Fahrenheit. - - ThermostatState (00002000): An enum representing the state of the thermostat heat call. + - ThermostatState: Thermostat State: 0 means idle, 1 means heating, 2 means pending + heat """ Unknown = auto() @@ -67,24 +63,11 @@ def default(cls) -> "TelemetryName": return cls.Unknown @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "001" if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") # noqa: TRY004 + raise TypeError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -103,82 +86,6 @@ def enum_version(cls) -> str: """ return "001" - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "Unknown". - """ - if symbol not in symbol_to_value: - return cls.default().value # noqa: ALL - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a TelemetryName enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "af39eec9", - "5a71d4b3", - "c89d0ba1", - "793505aa", - "d70cce28", - "ad19e79c", - "329a68c0", - "bb6fdd59", - "e0bb014b", - "337b8659", - "0f627faa", - "4c3f8c78", - "00002000", - ] - - -symbol_to_value = { - "00000000": "Unknown", - "af39eec9": "PowerW", - "5a71d4b3": "RelayState", - "c89d0ba1": "WaterTempCTimes1000", - "793505aa": "WaterTempFTimes1000", - "d70cce28": "GpmTimes100", - "ad19e79c": "CurrentRmsMicroAmps", - "329a68c0": "GallonsTimes100", - "bb6fdd59": "VoltageRmsMilliVolts", - "e0bb014b": "MilliWattHours", - "337b8659": "FrequencyMicroHz", - "0f627faa": "AirTempCTimes1000", - "4c3f8c78": "AirTempFTimes1000", - "00002000": "ThermostatState", -} - -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { "Unknown": "000", diff --git a/src/gwproto/enums/unit.py b/src/gwproto/enums/unit.py index b1e365bb..f7c08612 100644 --- a/src/gwproto/enums/unit.py +++ b/src/gwproto/enums/unit.py @@ -1,32 +1,31 @@ from enum import auto -from typing import List +from typing import Optional -from gwproto.enums.symbolized import SymbolizedEnum +from gw.enums import GwStrEnum -class Unit(SymbolizedEnum): +class Unit(GwStrEnum): """ Specifies the physical unit of sensed data reported by SCADA - Enum spaceheat.unit version 000 in the GridWorks Type registry. + Enum spaceheat.unit version 001 in the GridWorks Type registry. - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: + Used by multiple Application Shared Languages (ASLs). For more information: - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit) - Values (with symbols in parens): - - Unknown (00000000) - - Unitless (ec972387) - - W (f459a9c3) - - Celcius (ec14bd47) - - Fahrenheit (7d8832f8) - - Gpm (b4580361) - - WattHours (d66f1622) - - AmpsRms (a969ac7c) - - VoltsRms (e5d7555c) - - Gallons (8e123a26) - - ThermostatStateEnum (00003000) + Values: + - Unknown + - Unitless + - W + - Celcius + - Fahrenheit + - Gpm + - WattHours + - AmpsRms + - VoltsRms + - Gallons + - ThermostatStateEnum """ Unknown = auto() @@ -49,24 +48,11 @@ def default(cls) -> "Unit": return cls.Unknown @classmethod - def version(cls, value: str) -> str: - """ - Returns the version of an enum value. - - Once a value belongs to one version of the enum, it belongs - to all future versions. - - Args: - value (str): The candidate enum value. - - Raises: - ValueError: If value is not one of the enum values. - - Returns: - str: The earliest version of the enum containing value. - """ + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "001" if not isinstance(value, str): - raise ValueError("This method applies to strings, not enums") # noqa: TRY004 + raise TypeError("This method applies to strings, not enums") if value not in value_to_version: raise ValueError(f"Unknown enum value: {value}") return value_to_version[value] @@ -81,80 +67,10 @@ def enum_name(cls) -> str: @classmethod def enum_version(cls) -> str: """ - The version in the GridWorks Type Registry (000) + The version in the GridWorks Type Registry (001) """ - return "000" - - @classmethod - def symbol_to_value(cls, symbol: str) -> str: - """ - Given the symbol sent in a serialized message, returns the encoded enum. - - Args: - symbol (str): The candidate symbol. - - Returns: - str: The encoded value associated to that symbol. If the symbol is not - recognized - which could happen if the actor making the symbol is using - a later version of this enum, returns the default value of "Unknown". - """ - if symbol not in symbol_to_value: - return cls.default().value # noqa: ALL - return symbol_to_value[symbol] - - @classmethod - def value_to_symbol(cls, value: str) -> str: - """ - Provides the encoding symbol for a Unit enum to send in seriliazed messages. - - Args: - value (str): The candidate value. - - Returns: - str: The symbol encoding that value. If the value is not recognized - - which could happen if the actor making the message used a later version - of this enum than the actor decoding the message, returns the default - symbol of "00000000". - """ - if value not in value_to_symbol: - return value_to_symbol[cls.default().value] - return value_to_symbol[value] - - @classmethod - def symbols(cls) -> List[str]: - """ - Returns a list of the enum symbols - """ - return [ - "00000000", - "ec972387", - "f459a9c3", - "ec14bd47", - "7d8832f8", - "b4580361", - "d66f1622", - "a969ac7c", - "e5d7555c", - "8e123a26", - "00003000", - ] - - -symbol_to_value = { - "00000000": "Unknown", - "ec972387": "Unitless", - "f459a9c3": "W", - "ec14bd47": "Celcius", - "7d8832f8": "Fahrenheit", - "b4580361": "Gpm", - "d66f1622": "WattHours", - "a969ac7c": "AmpsRms", - "e5d7555c": "VoltsRms", - "8e123a26": "Gallons", - "00003000": "ThermostatStateEnum", -} + return "001" -value_to_symbol = {value: key for key, value in symbol_to_value.items()} value_to_version = { "Unknown": "000", @@ -167,5 +83,5 @@ def symbols(cls) -> List[str]: "AmpsRms": "000", "VoltsRms": "000", "Gallons": "000", - "ThermostatStateEnum": "000", + "ThermostatStateEnum": "001", } diff --git a/tests/enums/actor_class_test.py b/tests/enums/actor_class_test.py index dcd066d6..0189b349 100644 --- a/tests/enums/actor_class_test.py +++ b/tests/enums/actor_class_test.py @@ -19,6 +19,12 @@ def test_actor_class() -> None: "HubitatTelemetryReader", "HubitatTankModule", "HubitatPoller", + "I2cRelayMultiplexer", + "FlowTotalizer", + "Relay", + "Admin", + "Fsm", + "Parentless", "Hubitat", "HoneywellThermostat", } @@ -39,7 +45,11 @@ def test_actor_class() -> None: assert ActorClass.version("HubitatTelemetryReader") == "001" assert ActorClass.version("HubitatTankModule") == "001" assert ActorClass.version("HubitatPoller") == "001" - - for value in ActorClass.values(): - symbol = ActorClass.value_to_symbol(value) - assert ActorClass.symbol_to_value(symbol) == value + assert ActorClass.version("I2cRelayMultiplexer") == "001" + assert ActorClass.version("FlowTotalizer") == "001" + assert ActorClass.version("Relay") == "001" + assert ActorClass.version("Admin") == "001" + assert ActorClass.version("Fsm") == "001" + assert ActorClass.version("Parentless") == "001" + assert ActorClass.version("Hubitat") == "001" + assert ActorClass.version("HoneywellThermostat") == "001" diff --git a/tests/enums/local_comm_interface_test.py b/tests/enums/local_comm_interface_test.py deleted file mode 100644 index 8d95cdef..00000000 --- a/tests/enums/local_comm_interface_test.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Tests for enum local.comm.interface.000 from the GridWorks Type Registry. -""" - -from gwproto.enums import LocalCommInterface - - -def test_local_comm_interface() -> None: - assert set(LocalCommInterface.values()) == { - "UNKNOWN", - "I2C", - "ETHERNET", - "ONEWIRE", - "RS485", - "SIMRABBIT", - "WIFI", - "ANALOG_4_20_MA", - "RS232", - } - - assert LocalCommInterface.default() == LocalCommInterface.UNKNOWN - assert LocalCommInterface.enum_name() == "local.comm.interface" - assert LocalCommInterface.enum_version() == "000" - - assert LocalCommInterface.version("UNKNOWN") == "000" - assert LocalCommInterface.version("I2C") == "000" - assert LocalCommInterface.version("ETHERNET") == "000" - assert LocalCommInterface.version("ONEWIRE") == "000" - assert LocalCommInterface.version("RS485") == "000" - assert LocalCommInterface.version("SIMRABBIT") == "000" - assert LocalCommInterface.version("WIFI") == "000" - assert LocalCommInterface.version("ANALOG_4_20_MA") == "000" - assert LocalCommInterface.version("RS232") == "000" - - for value in LocalCommInterface.values(): - symbol = LocalCommInterface.value_to_symbol(value) - assert LocalCommInterface.symbol_to_value(symbol) == value diff --git a/tests/enums/make_model_test.py b/tests/enums/make_model_test.py index 8409c212..f28c1ae7 100644 --- a/tests/enums/make_model_test.py +++ b/tests/enums/make_model_test.py @@ -1,5 +1,5 @@ """ -Tests for enum spaceheat.make.model.001 from the GridWorks Type Registry. +Tests for enum spaceheat.make.model.002 from the GridWorks Type Registry. """ from gwproto.enums import MakeModel @@ -27,11 +27,28 @@ def test_make_model() -> None: "MAGNELAB__SCT0300050", "GRIDWORKS__MULTITEMP1", "KRIDA__EMR16I2CV3", + "OMEGA__FTB8007HWPT", + "ISTEC_4440", + "OMEGA__FTB8010HWPT", + "BELIMO__BALLVALVE232VS", + "BELIMO__DIVERTERB332L", + "TACO__0034EPLUS", + "TACO__007E", + "ARMSTRONG__COMPASSH", + "HONEYWELL__T6ZWAVETHERMOSTAT", + "PRMFILTRATION__WM075", + "BELLGOSSETT__ECOCIRC20_18", + "TEWA__TT0P10KC3T1051500", + "EKM__HOTSPWM075HD", + "GRIDWORKS__SIMMULTITEMP", + "GRIDWORKS__SIMTOTALIZER", + "KRIDA__DOUBLEEMR16I2CV3", + "GRIDWORKS__SIMDOUBLE16PINI2CRELAY", } assert MakeModel.default() == MakeModel.UNKNOWNMAKE__UNKNOWNMODEL assert MakeModel.enum_name() == "spaceheat.make.model" - assert MakeModel.enum_version() == "001" + assert MakeModel.enum_version() == "002" assert MakeModel.version("UNKNOWNMAKE__UNKNOWNMODEL") == "000" assert MakeModel.version("EGAUGE__4030") == "000" @@ -53,7 +70,20 @@ def test_make_model() -> None: assert MakeModel.version("MAGNELAB__SCT0300050") == "001" assert MakeModel.version("GRIDWORKS__MULTITEMP1") == "001" assert MakeModel.version("KRIDA__EMR16I2CV3") == "001" - - for value in MakeModel.values(): - symbol = MakeModel.value_to_symbol(value) - assert MakeModel.symbol_to_value(symbol) == value + assert MakeModel.version("OMEGA__FTB8007HWPT") == "002" + assert MakeModel.version("ISTEC_4440") == "002" + assert MakeModel.version("OMEGA__FTB8010HWPT") == "002" + assert MakeModel.version("BELIMO__BALLVALVE232VS") == "002" + assert MakeModel.version("BELIMO__DIVERTERB332L") == "002" + assert MakeModel.version("TACO__0034EPLUS") == "002" + assert MakeModel.version("TACO__007E") == "002" + assert MakeModel.version("ARMSTRONG__COMPASSH") == "002" + assert MakeModel.version("HONEYWELL__T6ZWAVETHERMOSTAT") == "002" + assert MakeModel.version("PRMFILTRATION__WM075") == "002" + assert MakeModel.version("BELLGOSSETT__ECOCIRC20_18") == "002" + assert MakeModel.version("TEWA__TT0P10KC3T1051500") == "002" + assert MakeModel.version("EKM__HOTSPWM075HD") == "002" + assert MakeModel.version("GRIDWORKS__SIMMULTITEMP") == "002" + assert MakeModel.version("GRIDWORKS__SIMTOTALIZER") == "002" + assert MakeModel.version("KRIDA__DOUBLEEMR16I2CV3") == "002" + assert MakeModel.version("GRIDWORKS__SIMDOUBLE16PINI2CRELAY") == "002" diff --git a/tests/enums/role_test.py b/tests/enums/role_test.py deleted file mode 100644 index f50db8ac..00000000 --- a/tests/enums/role_test.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Tests for enum sh.node.role.000 from the GridWorks Type Registry. -""" - -from gwproto.enums import Role - - -def test_role() -> None: - assert set(Role.values()) == { - "Unknown", - "Scada", - "HomeAlone", - "Atn", - "PowerMeter", - "BoostElement", - "BooleanActuator", - "DedicatedThermalStore", - "TankWaterTempSensor", - "PipeTempSensor", - "RoomTempSensor", - "OutdoorTempSensor", - "PipeFlowMeter", - "HeatedSpace", - "HydronicPipe", - "BaseboardRadiator", - "RadiatorFan", - "CirculatorPump", - "MultiChannelAnalogTempSensor", - "Outdoors", - } - - assert Role.default() == Role.Unknown - assert Role.enum_name() == "sh.node.role" - assert Role.enum_version() == "000" - - assert Role.version("Unknown") == "000" - assert Role.version("Scada") == "000" - assert Role.version("HomeAlone") == "000" - assert Role.version("Atn") == "000" - assert Role.version("PowerMeter") == "000" - assert Role.version("BoostElement") == "000" - assert Role.version("BooleanActuator") == "000" - assert Role.version("DedicatedThermalStore") == "000" - assert Role.version("TankWaterTempSensor") == "000" - assert Role.version("PipeTempSensor") == "000" - assert Role.version("RoomTempSensor") == "000" - assert Role.version("OutdoorTempSensor") == "000" - assert Role.version("PipeFlowMeter") == "000" - assert Role.version("HeatedSpace") == "000" - assert Role.version("HydronicPipe") == "000" - assert Role.version("BaseboardRadiator") == "000" - assert Role.version("RadiatorFan") == "000" - assert Role.version("CirculatorPump") == "000" - assert Role.version("MultiChannelAnalogTempSensor") == "000" - assert Role.version("Outdoors") == "000" - - for value in Role.values(): - symbol = Role.value_to_symbol(value) - assert Role.symbol_to_value(symbol) == value diff --git a/tests/enums/telemetry_name_test.py b/tests/enums/telemetry_name_test.py index b430b323..e31aaef3 100644 --- a/tests/enums/telemetry_name_test.py +++ b/tests/enums/telemetry_name_test.py @@ -40,7 +40,4 @@ def test_telemetry_name() -> None: assert TelemetryName.version("FrequencyMicroHz") == "001" assert TelemetryName.version("AirTempCTimes1000") == "001" assert TelemetryName.version("AirTempFTimes1000") == "001" - - for value in TelemetryName.values(): - symbol = TelemetryName.value_to_symbol(value) - assert TelemetryName.symbol_to_value(symbol) == value + assert TelemetryName.version("ThermostatState") == "001" diff --git a/tests/enums/unit_test.py b/tests/enums/unit_test.py index 7f96886e..fccc6217 100644 --- a/tests/enums/unit_test.py +++ b/tests/enums/unit_test.py @@ -1,5 +1,5 @@ """ -Tests for enum spaceheat.unit.000 from the GridWorks Type Registry. +Tests for enum spaceheat.unit.001 from the GridWorks Type Registry. """ from gwproto.enums import Unit @@ -22,7 +22,7 @@ def test_unit() -> None: assert Unit.default() == Unit.Unknown assert Unit.enum_name() == "spaceheat.unit" - assert Unit.enum_version() == "000" + assert Unit.enum_version() == "001" assert Unit.version("Unknown") == "000" assert Unit.version("Unitless") == "000" @@ -34,7 +34,4 @@ def test_unit() -> None: assert Unit.version("AmpsRms") == "000" assert Unit.version("VoltsRms") == "000" assert Unit.version("Gallons") == "000" - - for value in Unit.values(): - symbol = Unit.value_to_symbol(value) - assert Unit.symbol_to_value(symbol) == value + assert Unit.version("ThermostatStateEnum") == "001" From aa3f17ce341160c39c55a4265484cfbc58b18deb Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:33:30 -0400 Subject: [PATCH 119/168] Add Cacs By MakeModel axiom to cac gt; change ids so tests pass Delete tests for SimpleTempSensor and PipeFlowSensor as they are about to get tossed. --- .../types/component_attribute_class_gt.py | 73 +- tests/config/hardware-layout.json | 753 +++++------------- .../test_component_attribute_class_gt.py | 37 +- tests/types/test_component_gt.py | 15 +- tests/types/test_electric_meter_cac_gt.py | 5 +- .../types/test_electric_meter_component_gt.py | 4 +- .../types/test_multipurpose_sensor_cac_gt.py | 2 +- tests/types/test_pipe_flow_sensor_cac_gt.py | 17 - .../test_pipe_flow_sensor_component_gt.py | 29 - tests/types/test_relay_cac_gt.py | 17 - tests/types/test_relay_component_gt.py | 21 - tests/types/test_simple_temp_sensor_cac_gt.py | 23 - .../test_simple_temp_sensor_component_gt.py | 27 - 13 files changed, 308 insertions(+), 715 deletions(-) delete mode 100644 tests/types/test_pipe_flow_sensor_cac_gt.py delete mode 100644 tests/types/test_pipe_flow_sensor_component_gt.py delete mode 100644 tests/types/test_relay_cac_gt.py delete mode 100644 tests/types/test_relay_component_gt.py delete mode 100644 tests/types/test_simple_temp_sensor_cac_gt.py delete mode 100644 tests/types/test_simple_temp_sensor_component_gt.py diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index b4fae19a..181dc6d6 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -2,10 +2,12 @@ from typing import Literal, Optional -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict, model_validator +from typing_extensions import Self from gwproto.enums import MakeModel from gwproto.property_format import UUID4Str +from gwproto.type_helpers import CACS_BY_MAKE_MODEL class ComponentAttributeClassGt(BaseModel): @@ -14,3 +16,72 @@ class ComponentAttributeClassGt(BaseModel): MakeModel: MakeModel TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" + + model_config = ConfigDict(extra="allow") + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: Component Attribute Classes captured by MakeModel + If cac is a ComponentAttributeClassGt, + then + - EITHER its (MakeModel, ComponentAttributeClassId) must be a key,value pair in + CACS_BY_MAKE_MODEL (below) + - XOR its MakeModel is MakeModel.UNKNOWNMAKE__UNKNOWNMODEL + + CACS_BY_MAKE_MODEL: Dict[MakeModel, str] = { + MakeModel.EGAUGE__4030: '739a6e32-bb9c-43bc-a28d-fb61be665522', + MakeModel.NCD__PR814SPST: 'c6e736d8-8078-44f5-98bb-d72ca91dc773', + MakeModel.ADAFRUIT__642: '43564cd2-0e78-41a2-8b67-ad80c02161e8', + MakeModel.GRIDWORKS__WATERTEMPHIGHPRECISION: '7937eb7e-24d5-4d52-990f-cca063484df9', + MakeModel.GRIDWORKS__SIMPM1: '28897ac1-ea42-4633-96d3-196f63f5a951', + MakeModel.SCHNEIDERELECTRIC__IEM3455: '6bcdc388-de10-40e6-979a-8d66bfcfe9ba', + MakeModel.GRIDWORKS__SIMBOOL30AMPRELAY: '69f101fc-22e4-4caa-8103-50b8aeb66028', + MakeModel.OPENENERGY__EMONPI: '357b9b4f-2550-4380-aa6b-d2cd9c7ba0f9', + MakeModel.GRIDWORKS__SIMTSNAP1: 'b9f7135e-07a9-42f8-b847-a9bb3ea3770a', + MakeModel.ATLAS__EZFLO: '13d916dc-8764-4b16-b85d-b8ead3e2fc80', + MakeModel.HUBITAT__C7__LAN1: '62528da5-b510-4ac2-82c1-3782842eae07', + MakeModel.GRIDWORKS__TANK_MODULE_1: '60ac199d-679a-49f7-9142-8ca3e6428a5f', + MakeModel.FIBARO__ANALOG_TEMP_SENSOR: '7ce0ce69-14c6-4cb7-a33f-2aeca91e0680', + MakeModel.AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN: '2821c81d-054d-4003-9b07-2c295aef40f5', + MakeModel.YHDC__SCT013100: '812761ba-6544-4796-9aad-e1c979f58734', + MakeModel.MAGNELAB__SCT0300050: 'cf312bd6-7ca5-403b-a61b-b2e817ea1e22', + MakeModel.GRIDWORKS__MULTITEMP1: '432073b8-4d2b-4e36-9229-73893f33f846', + MakeModel.KRIDA__EMR16I2CV3: '018d9ffb-89d1-4cc4-95c0-f170711b5ffa', + MakeModel.OMEGA__FTB8007HWPT: '8cf6c726-e38a-4900-9cfe-ae6f053aafdf', + MakeModel.ISTEC_4440: '62ed724c-ba62-4302-ae30-d52b20d42ad9', + MakeModel.OMEGA__FTB8010HWPT: 'd9f225f8-eeb5-4cb7-b314-5551b925ea27', + MakeModel.BELIMO__BALLVALVE232VS: 'a2236d8c-7c9b-403f-9c55-733c62971d09', + MakeModel.BELIMO__DIVERTERB332L: 'f3261ed0-3fb1-4def-b60b-246960bf85ef', + MakeModel.TACO__0034EPLUS: '3880ba73-61e5-4b35-9df1-e154a03a3335', + MakeModel.TACO__007E: '198ebac8-e0b9-4cee-ae91-2ee6db708491', + MakeModel.ARMSTRONG__COMPASSH: 'ff6863e1-d5f7-4066-8579-2768162321a6', + MakeModel.HONEYWELL__T6ZWAVETHERMOSTAT: '03533a1f-3cb9-4a1f-8d57-690c0ad0475b', + MakeModel.PRMFILTRATION__WM075: '61d5c12d-eeca-4835-9a11-e61167d82e0d', + MakeModel.BELLGOSSETT__ECOCIRC20_18: '0d2ccc36-d2b8-405d-a257-3917111607c5', + MakeModel.TEWA__TT0P10KC3T1051500: '20779dbb-0302-4c36-9d60-e1962857c2f3', + MakeModel.EKM__HOTSPWM075HD: 'e52cb571-913a-4614-90f4-5cc81f8e7fe5', + MakeModel.GRIDWORKS__SIMMULTITEMP: '627ac482-24fe-46b2-ba8c-3d6f1e1ee069', + MakeModel.GRIDWORKS__SIMTOTALIZER: 'a88f8f4c-fe1e-4645-a7f4-249912131dc8', + MakeModel.KRIDA__DOUBLEEMR16I2CV3: '29eab8b1-100f-4230-bb44-3a2fcba33cc3'. + } + """ + if ( + self.MakeModel not in CACS_BY_MAKE_MODEL + and self.MakeModel is not MakeModel.default() + ): + raise ValueError( + "Axiom 1 violated! If MakeModel not in this list, " + f"must be UNKNOWN: {CACS_BY_MAKE_MODEL}" + ) + if self.MakeModel is MakeModel.default(): + if self.ComponentAttributeClassId in CACS_BY_MAKE_MODEL.values(): + raise ValueError( + f"Id {self.ComponentAttributeClassId} already used by known MakeModel!" + ) + elif self.ComponentAttributeClassId != CACS_BY_MAKE_MODEL[self.MakeModel]: + raise ValueError( + f"Axiom 1 violated! MakeModel {self.MakeModel} must have " + f"id {CACS_BY_MAKE_MODEL[self.MakeModel]}!" + ) + return self diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 61951421..fadb9776 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -1,4 +1,112 @@ { + "ElectricMeterCacs": [ + { + "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", + "MakeModel": "GRIDWORKS__SIMPM1", + "DisplayName": "Gridworks Pm1 Simulated Power Meter", + "Interface": "SIMRABBIT", + "PollPeriodMs": 1000, + "TelemetryNameList": ["PowerW"], + "TypeName": "electric.meter.cac.gt", + "Version": "000" + }, + { + "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", + "DisplayName": "EGauge 4030", + "MakeModel": "EGAUGE__4030", + "Interface": "ETHERNET", + "PollPeriodMs": 1000, + "TelemetryNameList": [ + "PowerW", + "MilliWattHours", + "VoltageRmsMilliVolts", + "CurrentRmsMicroAmps", + "FrequencyMicroHz" + ], + "TypeName": "electric.meter.cac.gt", + "Version": "000" + } + ], + "ElectricMeterComponents": [ + { + "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", + "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", + "DisplayName": "Power Meter for Simulated Test system", + "ConfigList": [ + { + "AboutNodeName": "a.elt1", + "ReportOnChange": true, + "SamplePeriodS": 300, + "Exponent": 0, + "AsyncReportThreshold": 0.02, + "NameplateMaxValue": 4500, + "TypeName": "telemetry.reporting.config", + "Version": "000", + "TelemetryName": "Power", + "Unit": "W" + } + ], + "HwUid": "1001ab", + "EgaugeIoList": [], + "TypeName": "electric.meter.component.gt", + "Version": "000" + } + ], + "MultipurposeSensorCacs": [ + { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "MakeModel": "GRIDWORKS__MULTITEMP1", + "PollPeriodMs": 200, + "Exponent": -3, + "TempUnit": "Celcius", + "TelemetryNameList": ["WaterTempCTimes1000"], + "MaxThermistors": 12, + "DisplayName": "Simulated GridWorks high precision water temp sensor", + "CommsMethod": "I2C", + "TypeName": "multipurpose.sensor.cac.gt", + "Version": "000" + } + ], + "MultipurposeSensorComponents": [ + { + "ChannelList": [ + 3, + 4 + ], + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "ComponentId": "fe18bffa-4b46-4b03-89d4-26b3d6f3e9d1", + "ConfigList": [ + { + "AboutNodeName": "hp.lwt", + "AsyncReportThreshold": 0.0025, + "Exponent": 3, + "NameplateMaxValue": 100000, + "ReportOnChange": true, + "SamplePeriodS": 60, + "TelemetryName": "WaterTempCTimes1000", + "TypeName": "telemetry.reporting.config", + "Unit": "Celsius", + "Version": "000" + }, + { + "AboutNodeName": "hp.ewt", + "AsyncReportThreshold": 0.0025, + "Exponent": 3, + "NameplateMaxValue": 100000, + "ReportOnChange": true, + "SamplePeriodS": 60, + "TelemetryName": "WaterTempCTimes1000", + "TypeName": "telemetry.reporting.config", + "Unit": "Celsius", + "Version": "000" + } + ], + "DisplayName": "Multipurpose Temp Sensor Component <100>", + "HwUid": "100", + "TypeName": "multipurpose.sensor.component.gt", + "Version": "000" + } + ], "MyAtomicTNodeGNode": { "GNodeId": "c4c9a280-453f-4c36-a081-970a3774b3ed", "Alias": "d1.isone.ct.newhaven.orange1", @@ -20,6 +128,76 @@ "GNodeStatusValue": "Active", "PrimaryGNodeRoleAlias": "Scada" }, + "ResistiveHeaterComponents": [ + { + "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", + "DisplayName": "First 4.5 kW boost in tank", + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "TypeName": "resistive.heater.component.gt", + "Version": "000", + "HwUid": "aaaa2222", + "TestedMaxColdMilliOhms": 14500, + "TestedMaxHotMilliOhms": 13714 + }, + { + "ComponentId": "d5fbf9f4-18a5-48f8-abdf-919309424321", + "DisplayName": "Second 4.5 kW boost in tank", + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "TypeName": "resistive.heater.component.gt", + "Version": "000", + "HwUid": "bbbb2222" + } + ], + "OtherComponents": [], + "ResistiveHeaterCacs": [ + { + "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "DisplayName": "Fake Boost Element", + "NameplateMaxPowerW": 4500, + "RatedVoltageV": 240, + "TypeName": "resistive.heater.cac.gt", + "Version": "000" + } + ], + "OtherCacs": [ + { + "ComponentAttributeClassId": "56498ea1-64b8-4102-b76f-4e29555d4d60", + "DisplayName": "Web Server CAC", + "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "TypeName": "component.attribute.class.gt", + "Version": "000" + }, + { + "ComponentAttributeClassId": "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", + "DisplayName": "Fibaro SmartImplant FGBS-222", + "MakeModel": "FIBARO__ANALOG_TEMP_SENSOR", + "Model": "FGBS-222 v5.2", + "TypeName": "component.attribute.class.gt", + "Version": "000" + }, + { + "ComponentAttributeClassId": "62528da5-b510-4ac2-82c1-3782842eae07", + "DisplayName": "Hubitat Elevation C-7", + "MakeModel": "HUBITAT__C7__LAN1", + "TypeName": "component.attribute.class.gt", + "Version": "000" + }, + { + "ComponentAttributeClassId": "60ac199d-679a-49f7-9142-8ca3e6428a5f", + "DisplayName": "Hubitat Tank Module", + "MakeModel": "GRIDWORKS__TANK_MODULE_1", + "TypeName": "component.attribute.class.gt", + "Version": "000" + }, + { + "ComponentAttributeClassId": "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", + "DisplayName": "Honeywell Z-Wave T6 Thermostat", + "MakeModel": "HONEYWELL__T6ZWAVETHERMOSTAT", + "TypeName": "component.attribute.class.gt", + "Version": "000" + } + ], "ShNodes": [ { "Alias": "a", @@ -31,7 +209,7 @@ "Version": "100" }, { - "Alias": "a.home", + "Alias": "home", "Role": "HomeAlone", "ActorClass": "HomeAlone", "DisplayName": "Little Orange House HomeAlone", @@ -40,7 +218,7 @@ "Version": "100" }, { - "Alias": "a.s", + "Alias": "s", "Role": "Scada", "ActorClass": "Scada", "DisplayName": "Little Orange House Main Scada", @@ -49,7 +227,7 @@ "Version": "100" }, { - "Alias": "a.elt1", + "Alias": "elt1", "Role": "BoostElement", "ActorClass": "NoActor", "DisplayName": "First boost element", @@ -62,50 +240,7 @@ "Version": "100" }, { - "Alias": "a.elt1.relay", - "Role": "BooleanActuator", - "ActorClass": "NoActor", - "DisplayName": "30A Relay for first boost element", - "ShNodeId": "e077df5d-7aea-4b9c-bfd7-773c1e65bd65", - "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.elt2", - "Role": "BoostElement", - "ActorClass": "NoActor", - "DisplayName": "Second boost element", - "ShNodeId": "ad51bb8f-f1bb-4a16-8652-76ed699c6a19", - "ComponentId": "d5fbf9f4-18a5-48f8-abdf-919309424321", - "RatedVoltageV": 240, - "TypicalVoltageV": 225, - "InPowerMetering": true, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.elt2.relay", - "Role": "BooleanActuator", - "ActorClass": "NoActor", - "DisplayName": "30A Relay for second boost element", - "ShNodeId": "d2eb5c93-ba1c-4f2c-a7ad-4dee995eb811", - "ComponentId": "58c4ad4f-6b1a-4fbf-80dd-d269f31e5ba5", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank", - "Role": "DedicatedThermalStore", - "ActorClass": "NoActor", - "DisplayName": "Little Orange House Test Axeman Tank", - "ShNodeId": "e9cbe9bf-7129-4508-b4d9-90d3af51b013", - "ComponentId": "d4a27899-98a3-409f-8c6a-771d86d7937b", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.m", + "Alias": "power.meter", "Role": "PowerMeter", "ActorClass": "PowerMeter", "DisplayName": "Main Power Meter Little Orange House Test System", @@ -115,530 +250,22 @@ "Version": "100" }, { - "Alias": "a.tank.out", - "Role": "HydronicPipe", - "ActorClass": "NoActor", - "DisplayName": "Main Heat Distribution Pipe Out of Tank", - "ShNodeId": "192a38da-fb2e-4233-a513-dc837f26e7f9", - "ComponentId": "1d62c7ce-b484-4610-a8b8-3979c8402588", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.in", - "Role": "HydronicPipe", - "ActorClass": "NoActor", - "DisplayName": "Main Heat Distribution Pipe into Tank", - "ShNodeId": "c9a80dca-8f60-4772-96f3-53793e64ef8a", - "ComponentId": "f4341350-973a-403a-98fe-dfd2a4acf038", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.pump", - "Role": "CirculatorPump", - "ActorClass": "NoActor", - "DisplayName": "Circulator Pump", - "ShNodeId": "f3db8728-1751-4f30-a739-d56bbff9971e", - "ComponentId": "1c207b38-d7bf-41cd-a9ad-6204bbab9922", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.pump.relay", - "Role": "BooleanActuator", - "ActorClass": "NoActor", - "DisplayName": "Circulator Pump relay", - "ShNodeId": "357a45f0-1a57-4f3d-9be4-4ff77cf19ba2", - "ComponentId": "e758eecd-6439-4e1d-9f66-534f6fc14859", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.pump.allradiators", - "Role": "BaseboardRadiator", - "ActorClass": "NoActor", - "DisplayName": "All the garage passive radiators", - "ShNodeId": "7d555c14-dcfe-43ba-9f50-92bd174c1d17", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.pump.allradiators.garage", - "Role": "HeatedSpace", - "ActorClass": "NoActor", - "DisplayName": "Little Orange House Garage", - "ShNodeId": "9db24e3b-10bf-4d8a-bb30-221230a90870", - "ComponentId": "f3a8c03b-ae00-4124-9c46-d98961636ddc", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.outdoors", - "Role": "Outdoors", - "ActorClass": "NoActor", - "DisplayName": "43 Avon St Microclimate", - "ShNodeId": "4dec0102-41b6-4255-ade6-c5b55dc16f01", - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.flowmeter1", - "Role": "PipeFlowMeter", - "ActorClass": "SimpleSensor", - "DisplayName": "Flow Meter on distribution pipe out of tank", - "ShNodeId": "fcc8cc94-75eb-42e1-8ef1-024961b053b7", - "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.temp1", - "Role": "PipeTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Temp Sensor on distribution pipe out of tank", - "ShNodeId": "66a4a5e7-f160-424a-8ad5-f6554bf9a99a", - "ComponentId": "a16d7bb6-2606-4ad1-b6b4-be80b5d84a6e", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.in.temp1", - "Role": "PipeTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Temp Sensor on distribution pipe into tank", - "ShNodeId": "d9c461f6-ed11-4dc1-9fe2-992d5d2413d0", - "ComponentId": "efabaa6a-e331-491f-9dbb-a3dee0029531", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.temp0", - "Role": "TankWaterTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Tank temp sensor temp0 (on top)", - "ShNodeId": "3593a10a-4335-447a-b62e-e123788a134a", - "ComponentId": "f516467e-7691-42c8-8525-f7d49bb135ce", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.temp1", - "Role": "TankWaterTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Tank temp sensor temp1 (second from top)", - "ShNodeId": "0df93059-f155-4c4f-a43c-0265a28f21fb", - "ComponentId": "d8d9f2b6-db8b-4a23-a037-20c7308ec56e", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.temp2", - "Role": "TankWaterTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Tank temp sensor temp2 (third from top)", - "ShNodeId": "17f84be9-6237-4331-91f8-17452eba0345", - "ComponentId": "5453b43b-7940-463c-ae6e-5cfe86ecbd3d", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.temp3", - "Role": "TankWaterTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Tank temp sensor temp3 (fourth from top)", - "ShNodeId": "296c9002-5771-4e11-969c-48a6db905b83", - "ComponentId": "17110718-b102-4c3b-ae62-5331aec9eed3", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.temp4", - "Role": "TankWaterTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "Tank temp sensor temp4 (fifth from top)", - "ShNodeId": "0680a809-d56f-42d8-8d98-e7c23e94d311", - "ComponentId": "bc2b731d-2b68-4cb8-9c27-8dd84dd2ea82", - "ReportingSamplePeriodS": 5, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.tank.out.pump.allradiators.garage", - "Role": "HeatedSpace", + "Alias": "hp.ewt", + "Role": "Unknown", "ActorClass": "NoActor", - "DisplayName": "Little Orange House Garage", - "ShNodeId": "26ed27a5-f362-4044-8bd7-19ce8acf4d63", + "DisplayName": "Heat Pump Entering Water Temp", + "ShNodeId": "e76073f4-67ae-4324-b07f-0c9add733de7", "TypeName": "spaceheat.node.gt", "Version": "100" }, { - "Alias": "a.tank.out.pump.allradiators.garage.temp1", - "Role": "RoomTempSensor", - "ActorClass": "SimpleSensor", - "DisplayName": "First Garage Temp sensor", - "ShNodeId": "2793ae37-e680-404e-a4ad-1efb0b8987cf", - "ComponentId": "23a09f17-741a-49b2-afbc-04f17f476594", - "ReportingSamplePeriodS": 60, - "TypeName": "spaceheat.node.gt", - "Version": "100" - }, - { - "Alias": "a.outdoors.temp1", - "Role": "5938bf1f", + "Alias": "hp.lwt", + "Role": "Unknown", "ActorClass": "NoActor", - "DisplayName": "First Outdoor Temp sensor", - "ShNodeId": "4c94cf37-dd87-49de-b782-aac97b69c154", - "ComponentId": "863359e1-f30a-4090-90a6-fc93154494a1", - "ReportingSamplePeriodS": 60, + "DisplayName": "Heat Pump Leaving Water Temp", + "ShNodeId": "1fe2a01f-d60a-4362-bc5e-949ddfcd5003", "TypeName": "spaceheat.node.gt", "Version": "100" } - ], - "ResistiveHeaterComponents": [ - { - "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", - "DisplayName": "First 4.5 kW boost in tank", - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "TypeName": "resistive.heater.component.gt", - "Version": "000", - "HwUid": "aaaa2222", - "TestedMaxColdMilliOhms": 14500, - "TestedMaxHotMilliOhms": 13714 - }, - { - "ComponentId": "d5fbf9f4-18a5-48f8-abdf-919309424321", - "DisplayName": "Second 4.5 kW boost in tank", - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "TypeName": "resistive.heater.component.gt", - "Version": "000", - "HwUid": "bbbb2222" - } - ], - "RelayComponents": [ - { - "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", - "DisplayName": "relay for simulated first elt in tank", - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "Gpio": 0, - "NormallyOpen": true, - "TypeName": "relay.component.gt", - "Version": "000" - }, - { - "ComponentId": "e758eecd-6439-4e1d-9f66-534f6fc14859", - "DisplayName": "relay for simulated main circulator pump", - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "Gpio": 1, - "NormallyOpen": false, - "TypeName": "relay.component.gt", - "Version": "000" - }, - { - "ComponentId": "58c4ad4f-6b1a-4fbf-80dd-d269f31e5ba5", - "DisplayName": "relay for second elt in tank", - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "Gpio": 2, - "NormallyOpen": true, - "TypeName": "relay.component.gt", - "Version": "000", - "HwUid": "ddd321" - } - ], - "MultipurposeSensorComponents": [], - "SimpleTempSensorComponents": [ - { - "ComponentId": "863359e1-f30a-4090-90a6-fc93154494a1", - "DisplayName": "Outdoor Temperature Sensor", - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "a16d7bb6-2606-4ad1-b6b4-be80b5d84a6e", - "DisplayName": "Temp sensor on pipe out of tank", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "efabaa6a-e331-491f-9dbb-a3dee0029531", - "DisplayName": "Temp sensor on pipe into tank", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "f516467e-7691-42c8-8525-f7d49bb135ce", - "DisplayName": "Component for a.tank.temp0 (on top)", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "HwUid": "1023abcd", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "d8d9f2b6-db8b-4a23-a037-20c7308ec56e", - "DisplayName": "Component for a.tank.temp1 (second from top)", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "HwUid": "10032fff", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "5453b43b-7940-463c-ae6e-5cfe86ecbd3d", - "DisplayName": "Component for a.tank.temp2 (third from top)", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "HwUid": "1004aaaa", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "17110718-b102-4c3b-ae62-5331aec9eed3", - "DisplayName": "Component for a.tank.temp3 (fourth from top)", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "HwUid": "100f2128", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "bc2b731d-2b68-4cb8-9c27-8dd84dd2ea82", - "DisplayName": "Component for a.tank.temp4 (fifth from top)", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "HwUid": "1004bbbb", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - }, - { - "ComponentId": "23a09f17-741a-49b2-afbc-04f17f476594", - "DisplayName": "First garage temp sensor", - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "HwUid": "1004aaaa", - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000" - } - ], - "ElectricMeterComponents": [ - { - "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", - "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "DisplayName": "Power Meter for Simulated Test system", - "ConfigList": [ - { - "AboutNodeName": "a.elt1", - "ReportOnChange": true, - "SamplePeriodS": 300, - "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "000", - "TelemetryName": "Power", - "Unit": "W" - }, - { - "AboutNodeName": "a.elt1", - "ReportOnChange": true, - "SamplePeriodS": 300, - "Exponent": 6, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 18750000, - "TypeName": "telemetry.reporting.config", - "Version": "000", - "TelemetryName": "CurrentRmsMicroAmps", - "Unit": "AmpsRms" - }, - { - "AboutNodeName": "a.elt2", - "ReportOnChange": true, - "SamplePeriodS": 300, - "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "000", - "TelemetryName": "Power", - "Unit": "W" - } - ], - "HwUid": "1001ab", - "EgaugeIoList": [], - "TypeName": "electric.meter.component.gt", - "Version": "000" - } - ], - "PipeFlowSensorComponents": [ - { - "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", - "DisplayName": "Flow meter on pipe out of tank", - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "TypeName": "pipe.flow.sensor.component.gt", - "Version": "000", - "I2cAddress": 100, - "ConversionFactor": 1.0, - "HwUid": "1234" - } - ], - "OtherComponents": [ - { - "ComponentId": "d4a27899-98a3-409f-8c6a-771d86d7937b", - "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "TypeName": "component.gt" - }, - { - "ComponentId": "1d62c7ce-b484-4610-a8b8-3979c8402588", - "DisplayName": "Hydronic pipe out of tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "TypeName": "component.gt" - }, - { - "ComponentId": "f4341350-973a-403a-98fe-dfd2a4acf038", - "DisplayName": "Hydronic pipe into tank", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "TypeName": "component.gt" - }, - { - "ComponentId": "1c207b38-d7bf-41cd-a9ad-6204bbab9922", - "DisplayName": "Circulator Pump", - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "TypeName": "component.gt" - }, - { - "ComponentId": "f3a8c03b-ae00-4124-9c46-d98961636ddc", - "DisplayName": "Little Orange House garage", - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "TypeName": "component.gt" - } - ], - "ResistiveHeaterCacs": [ - { - "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "DisplayName": "Fake Boost Element", - "NameplateMaxPowerW": 4500, - "RatedVoltageV": 240, - "TypeName": "resistive.heater.cac.gt", - "Version": "000" - } - ], - "RelayCacs": [ - { - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "MakeModel": "GRIDWORKS__SIMBOOL30AMPRELAY", - "TypicalResponseTimeMs": 400, - "DisplayName": "Gridworks Simulated Relay", - "TypeName": "relay.cac.gt", - "Version": "000" - } - ], - "PipeFlowSensorCacs": [ - { - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "CommsMethod": "Remove this Comms Method", - "DisplayName": "Some pipe flow sensor", - "TypeName": "pipe.flow.sensor.cac.gt", - "Version": "000" - } - ], - "ElectricMeterCacs": [ - { - "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "MakeModel": "GRIDWORKS__SIMPM1", - "DisplayName": "Gridworks Pm1 Simulated Power Meter", - "Interface": "SIMRABBIT", - "PollPeriodMs": 1000, - "TelemetryNameList": ["PowerW"], - "TypeName": "electric.meter.cac.gt", - "Version": "000" - }, - { - "ComponentAttributeClassId": "204832ef-0c88-408b-9640-264d2ee74914", - "MakeModel": "GRIDWORKS__SIMPM1", - "DisplayName": "Gridworks Pm1 Simulated Power Meter # 2", - "Interface": "SIMRABBIT", - "PollPeriodMs": 1000, - "TelemetryNameList": ["PowerW"], - "TypeName": "electric.meter.cac.gt", - "Version": "000" - } - ], - "MultipurposeSensorCacs": [ - { - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "MakeModel": "GRIDWORKS__MULTITEMP1", - "PollPeriodMs": 880, - "Exponent": -3, - "TempUnit": "Celcius", - "TelemetryNameList": ["WaterTempCTimes1000"], - "MaxThermistors": 12, - "DisplayName": "Simulated GridWorks high precision water temp sensor", - "CommsMethod": "I2C", - "TypeName": "multipurpose.sensor.cac.gt", - "Version": "000" - } - ], - "SimpleTempSensorCacs": [ - { - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "MakeModel": "GRIDWORKS__WATERTEMPHIGHPRECISION", - "DisplayName": "Simulated GridWorks high precision water temp sensor", - "CommsMethod": "SassyMQ", - "Exponent": -3, - "TempUnit": "Fahrenheit", - "TelemetryName": "WaterTempFTimes1000", - "TypicalResponseTimeMs": 880, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000" - }, - { - "ComponentAttributeClassId": "5450e92e-8c11-4383-b9b1-c8f412d83608", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TempUnit": "Celcius", - "TelemetryName": "WaterTempFTimes1000", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000" - }, - { - "ComponentAttributeClassId": "cac0f096-b460-4dce-aabf-a81ccce23566", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TempUnit": "Celcius", - "TelemetryName": "WaterTempFTimes1000", - "TypicalResponseTimeMs": 0, - "Exponent": -3, - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000" - } - ], - "OtherCacs": [ - { - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TypeName": "component.attribute.class.gt" - }, - { - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TypeName": "component.attribute.class.gt" - }, - { - "ComponentAttributeClassId": "f9a35cca-2b6d-418d-a81f-81f1c3d64776", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TypeName": "component.attribute.class.gt" - }, - { - "ComponentAttributeClassId": "c884aafe-99e0-4468-8bff-ffa74f672f9d", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", - "TypeName": "component.attribute.class.gt" - } ] } diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 12bc3a15..784947f5 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -1,17 +1,48 @@ """Tests component.attribute.class.gt type, version 000""" +import pytest +from pydantic import ValidationError + +from gwproto.enums import MakeModel +from gwproto.type_helpers import CACS_BY_MAKE_MODEL from gwproto.types import ComponentAttributeClassGt from tests.cac_load_utils import CacCase, assert_cac_load def test_component_attribute_class_gt_load() -> None: d = { - "ComponentAttributeClassId": "29c5257b-8a86-4dbe-a9d4-9c7330c3c4d0", - "DisplayName": "Sample CAC", - "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", + "ComponentAttributeClassId": "e52cb571-913a-4614-90f4-5cc81f8e7fe5", + "MakeModel": "EKM__HOTSPWM075HD", + "DisplayName": "EKM Hot-Spwm-075-HD Flow Meter", "TypeName": "component.attribute.class.gt", "Version": "000", } assert_cac_load( [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] ) + + # Test axiom 1 (Cac By Make Model) + random_uuid = "91567108-98ea-45af-aca5-f0026df3e131" + d2 = { + "ComponentAttributeClassId": random_uuid, + "MakeModel": "EKM__HOTSPWM075HD", + "DisplayName": "EKM Hot-Spwm-075-HD Flow Meter", + "MinPollPeriodMs": 1000, + "TypeName": "component.attribute.class.gt", + "Version": "001", + } + + with pytest.raises(ValidationError): + ComponentAttributeClassGt.model_validate(d2) + + d2 = { + "ComponentAttributeClassId": CACS_BY_MAKE_MODEL[MakeModel.ADAFRUIT__642], + "MakeModel": "EKM__HOTSPWM075HD", + "DisplayName": "EKM Hot-Spwm-075-HD Flow Meter", + "MinPollPeriodMs": 1000, + "TypeName": "component.attribute.class.gt", + "Version": "001", + } + + with pytest.raises(ValidationError): + ComponentAttributeClassGt.model_validate(d2) diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index 2f9fa3ad..a2f1f10f 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -1,17 +1,18 @@ -"""Tests component.gt type, version 000""" +"""Tests component.gt type, version 001""" -from gwproto.data_classes.components import Component from gwproto.types import ComponentGt -from tests.component_load_utils import ComponentCase, assert_component_load def test_component_gt_generated() -> None: d = { - "ComponentId": "987e0a5f-9036-411e-ba30-bac1075114ba", - "ComponentAttributeClassId": "cec0cb71-77bf-48a6-b644-2dcf124ac9fa", - "DisplayName": "Sample Component", + "ComponentId": "c6ec1ddb-5f51-4902-9807-a5ebc74d1102", + "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", + "DisplayName": "Demo eGauge Power Meter", "HwUid": "000aaa", "TypeName": "component.gt", "Version": "000", } - assert_component_load([ComponentCase("ComponentGt", d, ComponentGt, Component)]) + + d2 = ComponentGt.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index 298e74d0..c3aec33f 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -6,14 +6,11 @@ def test_electric_meter_cac_load() -> None: d = { - "ComponentAttributeClassId": "a3d298fb-a4ef-427a-939d-02cc9c9689c1", - # "MakeModelGtEnumSymbol": "d300635e", + "ComponentAttributeClassId": "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", "MakeModel": "SCHNEIDERELECTRIC__IEM3455", "DisplayName": "Schneider Electric Iem3455 Power Meter", - # "TelemetryNameList": ["af39eec9"], "TelemetryNameList": ["PowerW"], "PollPeriodMs": 1000, - # "InterfaceGtEnumSymbol": "a6a4ac9f", "Interface": "RS485", "DefaultBaud": 9600, "TypeName": "electric.meter.cac.gt", diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index e45b6eeb..e62b4670 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -1,14 +1,14 @@ """Tests electric.meter.component.gt type, version 000""" from gwproto.data_classes.components import ElectricMeterComponent -from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt +from gwproto.types import ElectricMeterComponentGt from tests.component_load_utils import ComponentCase, assert_component_load def test_electric_meter_component_gt_generated() -> None: d = { "ComponentId": "2dfb0cb6-6015-4273-b02b-bd446cc785d7", - "ComponentAttributeClassId": "204832ef-0c88-408b-9640-264d2ee74914", + "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", "DisplayName": "EGauge Power Meter", "ConfigList": [ { diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py index 2b404153..884a019f 100644 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ b/tests/types/test_multipurpose_sensor_cac_gt.py @@ -6,7 +6,7 @@ def test_multipurpose_sensor_cac_gt_load() -> None: d = { - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", "MakeModel": "GRIDWORKS__MULTITEMP1", "PollPeriodMs": 880, "Exponent": -3, diff --git a/tests/types/test_pipe_flow_sensor_cac_gt.py b/tests/types/test_pipe_flow_sensor_cac_gt.py deleted file mode 100644 index 2b5c3e47..00000000 --- a/tests/types/test_pipe_flow_sensor_cac_gt.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests pipe.flow.sensor.cac.gt type, version 000""" - -from gwproto.types import PipeFlowSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load - - -def test_pipe_flow_sensor_cac_gt_load() -> None: - d = { - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - # "MakeModelGtEnumSymbol": "d0b0e375", - "MakeModel": "ATLAS__EZFLO", - "DisplayName": "EZFLO for a.tank.out", - "CommsMethod": "I2C", - "TypeName": "pipe.flow.sensor.cac.gt", - "Version": "000", - } - assert_cac_load([CacCase("PipeFlowSensorCacGt", d, PipeFlowSensorCacGt)]) diff --git a/tests/types/test_pipe_flow_sensor_component_gt.py b/tests/types/test_pipe_flow_sensor_component_gt.py deleted file mode 100644 index 71e98e84..00000000 --- a/tests/types/test_pipe_flow_sensor_component_gt.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Tests pipe.flow.sensor.component.gt type, version 000""" - -from gwproto.data_classes.components import PipeFlowSensorComponent -from gwproto.types import PipeFlowSensorComponentGt -from tests.component_load_utils import ComponentCase, assert_component_load - - -def test_pipe_flow_sensor_component_gt_generated() -> None: - d = { - "ComponentId": "dd5ac673-91a8-40e2-a233-b67479cec709", - "ComponentAttributeClassId": "14e7105a-e797-485a-a304-328ecc85cd98", - "I2cAddress": 100, - "ConversionFactor": 0.1328, - "DisplayName": "Flow meter on pipe out of tank", - "HwUid": "1234", - "ExpectedMaxGpmTimes100": 1000, - "TypeName": "pipe.flow.sensor.component.gt", - "Version": "000", - } - assert_component_load( - [ - ComponentCase( - "PipeFlowSensorComponentGt", - d, - PipeFlowSensorComponentGt, - PipeFlowSensorComponent, - ) - ] - ) diff --git a/tests/types/test_relay_cac_gt.py b/tests/types/test_relay_cac_gt.py deleted file mode 100644 index 13aa3cd1..00000000 --- a/tests/types/test_relay_cac_gt.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Tests relay.cac.gt type, version 000""" - -from gwproto.types import RelayCacGt -from tests.cac_load_utils import CacCase, assert_cac_load - - -def test_relay_cac_gt_load() -> None: - d = { - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - # "MakeModelGtEnumSymbol": "9cc57878", - "MakeModel": "GRIDWORKS__SIMBOOL30AMPRELAY", - "DisplayName": "Gridworks Simulated Boolean Actuator", - "TypicalResponseTimeMs": 400, - "TypeName": "relay.cac.gt", - "Version": "000", - } - assert_cac_load([CacCase("RelayCacGt", d, RelayCacGt)]) diff --git a/tests/types/test_relay_component_gt.py b/tests/types/test_relay_component_gt.py deleted file mode 100644 index 8bf5f5ab..00000000 --- a/tests/types/test_relay_component_gt.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests relay.component.gt type, version 000""" - -from gwproto.data_classes.components import RelayComponent -from gwproto.types import RelayComponentGt -from tests.component_load_utils import ComponentCase, assert_component_load - - -def test_relay_component_gt_generated() -> None: - d = { - "ComponentId": "798fe14a-4073-41eb-bce2-075906aee6bb", - "ComponentAttributeClassId": "69f101fc-22e4-4caa-8103-50b8aeb66028", - "DisplayName": "relay for first elt in tank", - "Gpio": 0, - "HwUid": "abc123", - "NormallyOpen": True, - "TypeName": "relay.component.gt", - "Version": "000", - } - assert_component_load( - [ComponentCase("RelayComponentGt", d, RelayComponentGt, RelayComponent)], - ) diff --git a/tests/types/test_simple_temp_sensor_cac_gt.py b/tests/types/test_simple_temp_sensor_cac_gt.py deleted file mode 100644 index e2294ccc..00000000 --- a/tests/types/test_simple_temp_sensor_cac_gt.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Tests simple.temp.sensor.cac.gt type, version 000""" - -from gwproto.types import SimpleTempSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load - - -def test_simple_temp_sensor_cac_gt_load() -> None: - d = { - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - # "MakeModelGtEnumSymbol": "acd93fb3", - "MakeModel": "ADAFRUIT__642", - "TypicalResponseTimeMs": 880, - "Exponent": -3, - # "TempUnitGtEnumSymbol": "ec14bd47", - "TempUnit": "Celcius", - "TelemetryNameGtEnumSymbol": "WaterTempCTimes1000", - "TelemetryName": "", - "DisplayName": "Simulated GridWorks high precision water temp sensor", - "CommsMethod": "SassyMQ", - "TypeName": "simple.temp.sensor.cac.gt", - "Version": "000", - } - assert_cac_load([CacCase("SimpleTempSensorCacGt", d, SimpleTempSensorCacGt)]) diff --git a/tests/types/test_simple_temp_sensor_component_gt.py b/tests/types/test_simple_temp_sensor_component_gt.py deleted file mode 100644 index adff3b8d..00000000 --- a/tests/types/test_simple_temp_sensor_component_gt.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Tests simple.temp.sensor.component.gt type, version 000""" - -from gwproto.data_classes.components import SimpleTempSensorComponent -from gwproto.types import SimpleTempSensorComponentGt -from tests.component_load_utils import ComponentCase, assert_component_load - - -def test_simple_temp_sensor_component_gt_generated() -> None: - d = { - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ComponentAttributeClassId": "8a1a1538-ed2d-4829-9c03-f9be1c9f9c83", - "DisplayName": "Temp sensor on pipe out of tank", - "HwUid": "00033ffe", - "Channel": 0, - "TypeName": "simple.temp.sensor.component.gt", - "Version": "000", - } - assert_component_load( - [ - ComponentCase( - "SimpleTempSensorComponentGt", - d, - SimpleTempSensorComponentGt, - SimpleTempSensorComponent, - ) - ], - ) From bdc0343476d224dacfb1d2afc0a6bacb03717589 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 07:54:44 -0400 Subject: [PATCH 120/168] Updates post PR comments Use fully qualified paths in data classes Add back assert_component_load --- .../data_classes/components/electric_meter_component.py | 2 +- .../components/multipurpose_sensor_component.py | 2 +- tests/types/test_component_gt.py | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gwproto/data_classes/components/electric_meter_component.py b/src/gwproto/data_classes/components/electric_meter_component.py index fc0eb897..02710eb6 100644 --- a/src/gwproto/data_classes/components/electric_meter_component.py +++ b/src/gwproto/data_classes/components/electric_meter_component.py @@ -1,7 +1,7 @@ """ElectricMeterComponent definition""" from gwproto.data_classes.components.component import Component -from gwproto.types import ElectricMeterCacGt +from gwproto.types.electric_meter_cac_gt import ElectricMeterCacGt from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py index 2738e4c9..42f382a2 100644 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ b/src/gwproto/data_classes/components/multipurpose_sensor_component.py @@ -1,7 +1,7 @@ """MutlipurposeSensorComponent definition""" from gwproto.data_classes.components.component import Component -from gwproto.types import MultipurposeSensorCacGt +from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index a2f1f10f..87b18457 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -1,6 +1,8 @@ """Tests component.gt type, version 001""" +from gwproto.data_classes.components import Component from gwproto.types import ComponentGt +from tests.component_load_utils import ComponentCase, assert_component_load def test_component_gt_generated() -> None: @@ -13,6 +15,5 @@ def test_component_gt_generated() -> None: "Version": "000", } - d2 = ComponentGt.model_validate(d).model_dump(exclude_none=True) - - assert d2 == d + assert_component_load([ComponentCase("ComponentGt", d, ComponentGt, Component)]) + assert d == ComponentGt.model_validate(d).model_dump(exclude_none=True) From 7072f2d1d9baaea065b308cb06974df0d8a9e115 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 08:12:21 -0400 Subject: [PATCH 121/168] use enum values for cac --- src/gwproto/types/component_attribute_class_gt.py | 2 +- tests/types/test_component_attribute_class_gt.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 181dc6d6..1efbbbcb 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -17,7 +17,7 @@ class ComponentAttributeClassGt(BaseModel): TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" Version: Literal["000"] = "000" - model_config = ConfigDict(extra="allow") + model_config = ConfigDict(use_enum_values=True, extra="allow") @model_validator(mode="after") def check_axiom_1(self) -> Self: diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index 784947f5..b570e118 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -21,6 +21,8 @@ def test_component_attribute_class_gt_load() -> None: [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] ) + assert type(ComponentAttributeClassGt.model_validate(d).MakeModel) is str + # Test axiom 1 (Cac By Make Model) random_uuid = "91567108-98ea-45af-aca5-f0026df3e131" d2 = { From 4351ea9cf19d08135b71d0056cf5bde25e2f6ce7 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 08:22:02 -0400 Subject: [PATCH 122/168] use_enum_values - not wonderful --- .../type_helpers/cacs_by_make_model.py | 70 +++++++++---------- .../types/component_attribute_class_gt.py | 4 +- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/gwproto/type_helpers/cacs_by_make_model.py b/src/gwproto/type_helpers/cacs_by_make_model.py index 21cc1f39..21eb75e6 100644 --- a/src/gwproto/type_helpers/cacs_by_make_model.py +++ b/src/gwproto/type_helpers/cacs_by_make_model.py @@ -2,39 +2,39 @@ from gwproto.enums import MakeModel -CACS_BY_MAKE_MODEL: Dict[MakeModel, str] = { - MakeModel.EGAUGE__4030: "739a6e32-bb9c-43bc-a28d-fb61be665522", - MakeModel.NCD__PR814SPST: "c6e736d8-8078-44f5-98bb-d72ca91dc773", - MakeModel.ADAFRUIT__642: "43564cd2-0e78-41a2-8b67-ad80c02161e8", - MakeModel.GRIDWORKS__WATERTEMPHIGHPRECISION: "7937eb7e-24d5-4d52-990f-cca063484df9", - MakeModel.GRIDWORKS__SIMPM1: "28897ac1-ea42-4633-96d3-196f63f5a951", - MakeModel.SCHNEIDERELECTRIC__IEM3455: "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", - MakeModel.GRIDWORKS__SIMBOOL30AMPRELAY: "69f101fc-22e4-4caa-8103-50b8aeb66028", - MakeModel.OPENENERGY__EMONPI: "357b9b4f-2550-4380-aa6b-d2cd9c7ba0f9", - MakeModel.GRIDWORKS__SIMTSNAP1: "b9f7135e-07a9-42f8-b847-a9bb3ea3770a", - MakeModel.ATLAS__EZFLO: "13d916dc-8764-4b16-b85d-b8ead3e2fc80", - MakeModel.HUBITAT__C7__LAN1: "62528da5-b510-4ac2-82c1-3782842eae07", - MakeModel.GRIDWORKS__TANK_MODULE_1: "60ac199d-679a-49f7-9142-8ca3e6428a5f", - MakeModel.FIBARO__ANALOG_TEMP_SENSOR: "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", - MakeModel.AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN: "2821c81d-054d-4003-9b07-2c295aef40f5", - MakeModel.YHDC__SCT013100: "812761ba-6544-4796-9aad-e1c979f58734", - MakeModel.MAGNELAB__SCT0300050: "cf312bd6-7ca5-403b-a61b-b2e817ea1e22", - MakeModel.GRIDWORKS__MULTITEMP1: "432073b8-4d2b-4e36-9229-73893f33f846", - MakeModel.KRIDA__EMR16I2CV3: "018d9ffb-89d1-4cc4-95c0-f170711b5ffa", - MakeModel.OMEGA__FTB8007HWPT: "8cf6c726-e38a-4900-9cfe-ae6f053aafdf", - MakeModel.ISTEC_4440: "62ed724c-ba62-4302-ae30-d52b20d42ad9", - MakeModel.OMEGA__FTB8010HWPT: "d9f225f8-eeb5-4cb7-b314-5551b925ea27", - MakeModel.BELIMO__BALLVALVE232VS: "a2236d8c-7c9b-403f-9c55-733c62971d09", - MakeModel.BELIMO__DIVERTERB332L: "f3261ed0-3fb1-4def-b60b-246960bf85ef", - MakeModel.TACO__0034EPLUS: "3880ba73-61e5-4b35-9df1-e154a03a3335", - MakeModel.TACO__007E: "198ebac8-e0b9-4cee-ae91-2ee6db708491", - MakeModel.ARMSTRONG__COMPASSH: "ff6863e1-d5f7-4066-8579-2768162321a6", - MakeModel.HONEYWELL__T6ZWAVETHERMOSTAT: "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", - MakeModel.PRMFILTRATION__WM075: "61d5c12d-eeca-4835-9a11-e61167d82e0d", - MakeModel.BELLGOSSETT__ECOCIRC20_18: "0d2ccc36-d2b8-405d-a257-3917111607c5", - MakeModel.TEWA__TT0P10KC3T1051500: "20779dbb-0302-4c36-9d60-e1962857c2f3", - MakeModel.EKM__HOTSPWM075HD: "e52cb571-913a-4614-90f4-5cc81f8e7fe5", - MakeModel.GRIDWORKS__SIMMULTITEMP: "627ac482-24fe-46b2-ba8c-3d6f1e1ee069", - MakeModel.GRIDWORKS__SIMTOTALIZER: "a88f8f4c-fe1e-4645-a7f4-249912131dc8", - MakeModel.KRIDA__DOUBLEEMR16I2CV3: "29eab8b1-100f-4230-bb44-3a2fcba33cc3", +CACS_BY_MAKE_MODEL: Dict[str, str] = { + MakeModel.EGAUGE__4030.value: "739a6e32-bb9c-43bc-a28d-fb61be665522", + MakeModel.NCD__PR814SPST.value: "c6e736d8-8078-44f5-98bb-d72ca91dc773", + MakeModel.ADAFRUIT__642.value: "43564cd2-0e78-41a2-8b67-ad80c02161e8", + MakeModel.GRIDWORKS__WATERTEMPHIGHPRECISION.value: "7937eb7e-24d5-4d52-990f-cca063484df9", + MakeModel.GRIDWORKS__SIMPM1.value: "28897ac1-ea42-4633-96d3-196f63f5a951", + MakeModel.SCHNEIDERELECTRIC__IEM3455.value: "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", + MakeModel.GRIDWORKS__SIMBOOL30AMPRELAY.value: "69f101fc-22e4-4caa-8103-50b8aeb66028", + MakeModel.OPENENERGY__EMONPI.value: "357b9b4f-2550-4380-aa6b-d2cd9c7ba0f9", + MakeModel.GRIDWORKS__SIMTSNAP1.value: "b9f7135e-07a9-42f8-b847-a9bb3ea3770a", + MakeModel.ATLAS__EZFLO.value: "13d916dc-8764-4b16-b85d-b8ead3e2fc80", + MakeModel.HUBITAT__C7__LAN1.value: "62528da5-b510-4ac2-82c1-3782842eae07", + MakeModel.GRIDWORKS__TANK_MODULE_1.value: "60ac199d-679a-49f7-9142-8ca3e6428a5f", + MakeModel.FIBARO__ANALOG_TEMP_SENSOR.value: "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", + MakeModel.AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN.value: "2821c81d-054d-4003-9b07-2c295aef40f5", + MakeModel.YHDC__SCT013100.value: "812761ba-6544-4796-9aad-e1c979f58734", + MakeModel.MAGNELAB__SCT0300050.value: "cf312bd6-7ca5-403b-a61b-b2e817ea1e22", + MakeModel.GRIDWORKS__MULTITEMP1.value: "432073b8-4d2b-4e36-9229-73893f33f846", + MakeModel.KRIDA__EMR16I2CV3.value: "018d9ffb-89d1-4cc4-95c0-f170711b5ffa", + MakeModel.OMEGA__FTB8007HWPT.value: "8cf6c726-e38a-4900-9cfe-ae6f053aafdf", + MakeModel.ISTEC_4440.value: "62ed724c-ba62-4302-ae30-d52b20d42ad9", + MakeModel.OMEGA__FTB8010HWPT.value: "d9f225f8-eeb5-4cb7-b314-5551b925ea27", + MakeModel.BELIMO__BALLVALVE232VS.value: "a2236d8c-7c9b-403f-9c55-733c62971d09", + MakeModel.BELIMO__DIVERTERB332L.value: "f3261ed0-3fb1-4def-b60b-246960bf85ef", + MakeModel.TACO__0034EPLUS.value: "3880ba73-61e5-4b35-9df1-e154a03a3335", + MakeModel.TACO__007E.value: "198ebac8-e0b9-4cee-ae91-2ee6db708491", + MakeModel.ARMSTRONG__COMPASSH.value: "ff6863e1-d5f7-4066-8579-2768162321a6", + MakeModel.HONEYWELL__T6ZWAVETHERMOSTAT.value: "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", + MakeModel.PRMFILTRATION__WM075.value: "61d5c12d-eeca-4835-9a11-e61167d82e0d", + MakeModel.BELLGOSSETT__ECOCIRC20_18.value: "0d2ccc36-d2b8-405d-a257-3917111607c5", + MakeModel.TEWA__TT0P10KC3T1051500.value: "20779dbb-0302-4c36-9d60-e1962857c2f3", + MakeModel.EKM__HOTSPWM075HD.value: "e52cb571-913a-4614-90f4-5cc81f8e7fe5", + MakeModel.GRIDWORKS__SIMMULTITEMP.value: "627ac482-24fe-46b2-ba8c-3d6f1e1ee069", + MakeModel.GRIDWORKS__SIMTOTALIZER.value: "a88f8f4c-fe1e-4645-a7f4-249912131dc8", + MakeModel.KRIDA__DOUBLEEMR16I2CV3.value: "29eab8b1-100f-4230-bb44-3a2fcba33cc3", } diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index 1efbbbcb..aa1d5286 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -68,13 +68,13 @@ def check_axiom_1(self) -> Self: """ if ( self.MakeModel not in CACS_BY_MAKE_MODEL - and self.MakeModel is not MakeModel.default() + and self.MakeModel is not MakeModel.default().value ): raise ValueError( "Axiom 1 violated! If MakeModel not in this list, " f"must be UNKNOWN: {CACS_BY_MAKE_MODEL}" ) - if self.MakeModel is MakeModel.default(): + if self.MakeModel is MakeModel.default().value: if self.ComponentAttributeClassId in CACS_BY_MAKE_MODEL.values(): raise ValueError( f"Id {self.ComponentAttributeClassId} already used by known MakeModel!" From 0ed1a694bf2392dcf6998471c03ee3b3b34e49f5 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 10:47:23 -0400 Subject: [PATCH 123/168] remove gt_enum_symbol from hubitat objects --- docs/asls/json/hubitat-tank-component-gt.json | 61 ------------------- docs/asls/json/local-comm-interface.json | 52 ---------------- src/gwproto/types/hubitat_poller_gt.py | 32 +++------- src/gwproto/types/hubitat_tank_gt.py | 33 ++-------- 4 files changed, 13 insertions(+), 165 deletions(-) delete mode 100644 docs/asls/json/local-comm-interface.json diff --git a/docs/asls/json/hubitat-tank-component-gt.json b/docs/asls/json/hubitat-tank-component-gt.json index 680897b9..9dcb6c24 100644 --- a/docs/asls/json/hubitat-tank-component-gt.json +++ b/docs/asls/json/hubitat-tank-component-gt.json @@ -46,66 +46,5 @@ "default": "000", "required": true } - }, - "example": { - "ComponentAttributeClassId": "60ac199d-679a-49f7-9142-8ca3e6428a5f", - "ComponentId": "f26d412b-3918-427c-9bb9-cb17b7f2e7e4", - "DisplayName": "Oak Tank Module SN 1010", - "Tank": { - "devices": [ - { - "analog_input_id": 1, - "device_id": 103, - "enabled": true, - "exponent": 1, - "fibaro_component_id": "1fdd40dd-14d7-4da2-8cf8-7cf66484e385", - "rest": null, - "stack_depth": 1, - "tank_label": "1010 A1 (Thermistor #1 TANK TOP)", - "telemetry_name_gt_enum_symbol": "c89d0ba1", - "temp_unit_gt_enum_symbol": "ec14bd47" - }, - { - "analog_input_id": 2, - "device_id": 104, - "enabled": true, - "exponent": 1, - "fibaro_component_id": "1fdd40dd-14d7-4da2-8cf8-7cf66484e385", - "rest": null, - "stack_depth": 2, - "tank_label": "1010 A2 (Thermistor #2)", - "telemetry_name_gt_enum_symbol": "c89d0ba1", - "temp_unit_gt_enum_symbol": "ec14bd47" - }, - { - "analog_input_id": 1, - "device_id": 24, - "enabled": true, - "exponent": 1, - "fibaro_component_id": "a6241764-329d-462f-94f9-0283f707d195", - "rest": null, - "stack_depth": 3, - "tank_label": "1010 B1 (Thermistor #3)", - "telemetry_name_gt_enum_symbol": "c89d0ba1", - "temp_unit_gt_enum_symbol": "ec14bd47" - }, - { - "analog_input_id": 2, - "device_id": 25, - "enabled": true, - "exponent": 1, - "fibaro_component_id": "a6241764-329d-462f-94f9-0283f707d195", - "rest": null, - "stack_depth": 4, - "tank_label": "1010 B2 (Thermistor #4 TANK BOTTOM)", - "telemetry_name_gt_enum_symbol": "c89d0ba1", - "temp_unit_gt_enum_symbol": "ec14bd47" - } - ], - "hubitat_component_id": "48039704-7d45-4937-adda-0e362d13cef6", - "sensor_supply_voltage": 23.7 - }, - "TypeName": "hubitat.tank.component.gt", - "Version": "000" } } diff --git a/docs/asls/json/local-comm-interface.json b/docs/asls/json/local-comm-interface.json deleted file mode 100644 index fcff28ba..00000000 --- a/docs/asls/json/local-comm-interface.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "local.comm.interface", - "enum_version": "000", - "description": "Categorization of in-house comm mechanisms for SCADA", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#localcomminterface", - "values": [ - "Unknown", - "I2C", - "Ethernet", - "OneWire", - "RS485", - "SimRabbit", - "Wifi", - "Analog_4_20_mA", - "RS232" - ], - "value_to_gt_symbol": { - "UNKNOWN": "00000000", - "I2C": "9ec8bc49", - "ETHERNET": "c1e7a955", - "ONEWIRE": "ae2d4cd8", - "RS485": "a6a4ac9f", - "SIMRABBIT": "efc144cd", - "WIFI": "46ac6589", - "ANALOG_4_20_MA": "653c73b8", - "RS232": "0843a726" - }, - "value_to_version": { - "UNKNOWN": "000", - "I2C": "000", - "ETHERNET": "000", - "ONEWIRE": "000", - "RS485": "000", - "SIMRABBIT": "000", - "WIFI": "000", - "ANALOG_4_20_MA": "000", - "RS232": "000" - }, - "value_descriptions": { - "Unknown": "", - "I2C": "", - "Ethernet": "", - "OneWire": "", - "RS485": "", - "SimRabbit": "", - "Wifi": "", - "Analog_4_20_mA": "", - "RS232": "" - }, - "default_value": "Unknown" -} diff --git a/src/gwproto/types/hubitat_poller_gt.py b/src/gwproto/types/hubitat_poller_gt.py index eca5b186..a0e8cdcb 100644 --- a/src/gwproto/types/hubitat_poller_gt.py +++ b/src/gwproto/types/hubitat_poller_gt.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, ConfigDict, field_validator +from pydantic import BaseModel, ConfigDict from gwproto.enums import TelemetryName, Unit from gwproto.utils import snake_to_camel @@ -7,8 +7,8 @@ class MakerAPIAttributeGt(BaseModel): attribute_name: str node_name: str - telemetry_name_gt_enum_symbol: str = "c89d0ba1" - unit_gt_enum_symbol: str = "ec14bd47" + telemetry_name: TelemetryName = TelemetryName.WaterTempCTimes1000 + unit: Unit = Unit.Celcius exponent: int = 3 interpret_as_number: bool = True enabled: bool = True @@ -17,31 +17,13 @@ class MakerAPIAttributeGt(BaseModel): report_missing: bool = True report_parse_error: bool = True - @property - def telemetry_name(self) -> TelemetryName: - value = TelemetryName.symbol_to_value( - self.telemetry_name_gt_enum_symbol, - ) - return TelemetryName(value) - - @property - def unit(self) -> Unit: - value = Unit.symbol_to_value( - self.unit_gt_enum_symbol, - ) - return Unit(value) - model_config = ConfigDict( - extra="allow", alias_generator=snake_to_camel, populate_by_name=True + extra="allow", + use_enum_values=True, + alias_generator=snake_to_camel, + populate_by_name=True, ) - @field_validator("telemetry_name_gt_enum_symbol") - @classmethod - def _check_telemetry_name_symbol(cls, v: str) -> str: - if v not in TelemetryName.symbols(): - v = TelemetryName.value_to_symbol(TelemetryName.default()) - return v - class HubitatPollerGt(BaseModel): hubitat_component_id: str diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index 50371971..cf6bfebd 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -33,8 +33,8 @@ class FibaroTempSensorSettingsGt(BaseModel): analog_input_id: Annotated[int, Field(ge=1, le=2)] tank_label: str = "" exponent: int = 1 - telemetry_name_gt_enum_symbol: str = "c89d0ba1" - temp_unit_gt_enum_symbol: str = "ec14bd47" + telemetry_name: TelemetryName = TelemetryName.WaterTempCTimes1000 + temp_unit: Unit = Unit.Celcius enabled: bool = True web_listen_enabled: bool = True poll_period_seconds: Optional[float] = None @@ -46,23 +46,12 @@ class FibaroTempSensorSettingsGt(BaseModel): """ rest: Optional[RESTPollerSettings] = None model_config = ConfigDict( - extra="allow", alias_generator=snake_to_camel, populate_by_name=True + extra="allow", + use_enum_values=True, + alias_generator=snake_to_camel, + populate_by_name=True, ) - @field_validator("telemetry_name_gt_enum_symbol") - @classmethod - def _check_telemetry_name_symbol(cls, v: str) -> str: - if v not in TelemetryName.symbols(): - v = TelemetryName.value_to_symbol(TelemetryName.default()) - return v - - @field_validator("temp_unit_gt_enum_symbol") - @classmethod - def _checktemp_unit_gt_enum_symbol(cls, v: str) -> str: - if v not in Unit.symbols(): - v = Unit.value_to_symbol(Unit.default()) - return v - DEFAULT_SENSOR_NODE_NAME_FORMAT = "{tank_name}.temp.depth{stack_depth}" @@ -84,16 +73,6 @@ def _collapse_rest_url( v.request.url.url_args = URLArgs.from_url(collapsed_url) return v - @property - def telemetry_name(self) -> TelemetryName: - value = TelemetryName.symbol_to_value(self.telemetry_name_gt_enum_symbol) - return TelemetryName(value) - - @property - def unit(self) -> Unit: - value = Unit.symbol_to_value(self.temp_unit_gt_enum_symbol) - return Unit(value) - @cached_property def url(self) -> yarl.URL: return self.rest.url From 61ca8d2cd66f01c870b98995c2acb7090fc3fba9 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 18:17:05 -0400 Subject: [PATCH 124/168] remove gs messages, add data channels --- docs/sdk-types.rst | 1 - docs/types/data-channel-gt.rst | 67 +++++++++ docs/types/data-channel.rst | 35 ----- docs/types/ta-data-channels.rst | 51 ------- src/gwproto/data_classes/data_channel.py | 14 ++ src/gwproto/gs/__init__.py | 9 -- src/gwproto/gs/gs_dispatch.py | 14 -- src/gwproto/gs/gs_dispatch_base.py | 24 ---- src/gwproto/gs/gs_dispatch_maker.py | 26 ---- src/gwproto/gs/gs_pwr.py | 14 -- src/gwproto/gs/gs_pwr_base.py | 26 ---- src/gwproto/gs/gs_pwr_maker.py | 26 ---- src/gwproto/messages/__init__.py | 5 - src/gwproto/property_format.py | 164 ++++++++++++++++++++--- src/gwproto/types/__init__.py | 6 +- src/gwproto/types/data_channel.py | 31 ----- src/gwproto/types/data_channel_gt.py | 39 ++++++ src/gwproto/types/ta_data_channels.py | 19 --- tests/test_decoders.py | 4 +- tests/test_gs_dispatch.py | 17 --- tests/test_gs_pwr.py | 17 --- tests/types/test_data_channel.py | 15 --- tests/types/test_data_channel_gt.py | 33 +++++ tests/types/test_ta_data_channels.py | 26 ---- 24 files changed, 299 insertions(+), 384 deletions(-) create mode 100644 docs/types/data-channel-gt.rst delete mode 100644 docs/types/data-channel.rst delete mode 100644 docs/types/ta-data-channels.rst create mode 100644 src/gwproto/data_classes/data_channel.py delete mode 100644 src/gwproto/gs/__init__.py delete mode 100644 src/gwproto/gs/gs_dispatch.py delete mode 100644 src/gwproto/gs/gs_dispatch_base.py delete mode 100644 src/gwproto/gs/gs_dispatch_maker.py delete mode 100644 src/gwproto/gs/gs_pwr.py delete mode 100644 src/gwproto/gs/gs_pwr_base.py delete mode 100644 src/gwproto/gs/gs_pwr_maker.py delete mode 100644 src/gwproto/types/data_channel.py create mode 100644 src/gwproto/types/data_channel_gt.py delete mode 100644 src/gwproto/types/ta_data_channels.py delete mode 100644 tests/test_gs_dispatch.py delete mode 100644 tests/test_gs_pwr.py delete mode 100644 tests/types/test_data_channel.py create mode 100644 tests/types/test_data_channel_gt.py delete mode 100644 tests/types/test_ta_data_channels.py diff --git a/docs/sdk-types.rst b/docs/sdk-types.rst index cdb02cb2..824babdb 100644 --- a/docs/sdk-types.rst +++ b/docs/sdk-types.rst @@ -59,6 +59,5 @@ forth between type instances and Python objects. SimpleTempSensorComponentGt SnapshotSpaceheat SpaceheatNodeGt - TaDataChannels TelemetryReportingConfig TelemetrySnapshotSpaceheat diff --git a/docs/types/data-channel-gt.rst b/docs/types/data-channel-gt.rst new file mode 100644 index 00000000..325d46da --- /dev/null +++ b/docs/types/data-channel-gt.rst @@ -0,0 +1,67 @@ +DataChannelGt +========================== +Python pydantic class corresponding to json type `data.channel.gt`, version `001`. + +.. autoclass:: gwproto.types.DataChannelGt + :members: + +**Name**: + - Description: Name. The Channel Name is meant to be the local unique identifier for the channel within the context of a specific TerminalAsset. In addition to local uniqueness, it is immutable. It is designed to be the key that time series data is sorted by in analysis, as well as a useful way of referencing a channel within Scada code. + - Format: SpaceheatName + +**DisplayName**: + - Description: Display Name. This display name is the handle for the data channel. It is meant to be set by the person/people who will be analyzing time series data. It is only expected to be unique within the data channels associated to a specific Terminal Asset. Mutable. + +**AboutNodeName**: + - Description: About Name. The name of the SpaceheatNode whose physical quantities are getting captured. + - Format: SpaceheatName + +**CapturedByNodeName**: + - Description: Captured By Name. The name of the SpaceheatNode that is capturing the physical quantities (which can be AboutName but does not have to be). + - Format: SpaceheatName + +**TelemetryName**: + - Description: Telemetry Name. The name of the physical quantity getting measured. + +**TerminalAssetAlias**: + - Description: Terminal Asset. The Terminal Asset GNode for which this data channel is reporting data. For example, the GNode with alias hw1.isone.me.versant.keene.beech.ta represents the heat pump thermal storage system in the first GridWorks Millinocket deployment. + - Format: LeftRightDot + +**InPowerMetering**: + - Description: In Power Metering. This channel is in the sum of the aggregate transactive power metering for the terminal asset + +**StartS**: + - Description: Start Seconds Epoch Time. The epoch time of the first data record associated to a channel. If this value is None it means no known data yet. + - Format: UTCSeconds + +**Id**: + - Description: Id. Meant to be an immutable identifier that is globally unique (i.e., across terminal assets). + - Format: UUID4Str + +**TypeName**: + - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. + +**Version**: + - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. + + + +.. autoclass:: gwproto.types.data_channel_gt.check_is_u_t_c_seconds + :members: + + +.. autoclass:: gwproto.types.data_channel_gt.check_is_u_u_i_d4_str + :members: + + +.. autoclass:: gwproto.types.data_channel_gt.check_is_spaceheat_name + :members: + + +.. autoclass:: gwproto.types.data_channel_gt.check_is_left_right_dot + :members: + + +.. autoclass:: gwproto.types.DataChannelGt_Maker + :members: + diff --git a/docs/types/data-channel.rst b/docs/types/data-channel.rst deleted file mode 100644 index 8a08ab61..00000000 --- a/docs/types/data-channel.rst +++ /dev/null @@ -1,35 +0,0 @@ -DataChannel -========================== -Python pydantic class corresponding to json type `data.channel`, version `000`. - -.. autoclass:: gwproto.types.DataChannel - :members: - -**DisplayName**: - - Description: Display Name. This display name is the handle for the data channel. It is meant to be set by the person/people who will be analyzing time series data. It is only expected to be unique within the data channels associated to a specific Terminal Asset. - -**AboutName**: - - Description: About Name. The name of the SpaceheatNode whose physical quantities are getting captured. - - Format: SpaceheatName - -**CapturedByName**: - - Description: The name of the SpaceheatNode that is capturing the physical quantities (which can be AboutName but does not have to be). - - Format: SpaceheatName - -**TelemetryName**: - - Description: The name of the physical quantity getting measured. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.data_channel.check_is_spaceheat_name - :members: - - -.. autoclass:: gwproto.types.DataChannel_Maker - :members: diff --git a/docs/types/ta-data-channels.rst b/docs/types/ta-data-channels.rst deleted file mode 100644 index 9e8ec462..00000000 --- a/docs/types/ta-data-channels.rst +++ /dev/null @@ -1,51 +0,0 @@ -TaDataChannels -========================== -Python pydantic class corresponding to json type `ta.data.channels`, version `000`. - -.. autoclass:: gwproto.types.TaDataChannels - :members: - -**TerminalAssetGNodeAlias**: - - Description: GNodeAlias for the Terminal Asset. The Alias of the Terminal Asset about which the time series data is providing information. - - Format: LeftRightDot - -**TerminalAssetGNodeId**: - - Description: GNodeId for the Terminal Asset. The immutable unique identifier for the Terminal Asset. - - Format: UuidCanonicalTextual - -**TimeUnixS**: - - Description: TimeUnixS. The time that this list of data channels was created - - Format: ReasonableUnixTimeS - -**Author**: - - Description: Author of this list of data channels. - -**Channels**: - - Description: The list of data channels. - -**Identifier**: - - Description: Identifier. Unique identifier for a specific instance of this type that can be used to establish how time series csv's were constructed. - - Format: UuidCanonicalTextual - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.ta_data_channels.check_is_reasonable_unix_time_s - :members: - - -.. autoclass:: gwproto.types.ta_data_channels.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.ta_data_channels.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.TaDataChannels_Maker - :members: diff --git a/src/gwproto/data_classes/data_channel.py b/src/gwproto/data_classes/data_channel.py new file mode 100644 index 00000000..9e193741 --- /dev/null +++ b/src/gwproto/data_classes/data_channel.py @@ -0,0 +1,14 @@ +from pydantic import ConfigDict + +from gwproto.data_classes.sh_node import ShNode +from gwproto.types import DataChannelGt + + +class DataChannel(DataChannelGt): + about_node: ShNode + captured_by_node: ShNode + + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __hash__(self) -> int: + return hash(self.Id) diff --git a/src/gwproto/gs/__init__.py b/src/gwproto/gs/__init__.py deleted file mode 100644 index 0ef22042..00000000 --- a/src/gwproto/gs/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .gs_dispatch_maker import GsDispatch, GsDispatch_Maker -from .gs_pwr_maker import GsPwr, GsPwr_Maker - -__all__ = [ - "GsDispatch", - "GsDispatch_Maker", - "GsPwr", - "GsPwr_Maker", -] diff --git a/src/gwproto/gs/gs_dispatch.py b/src/gwproto/gs/gs_dispatch.py deleted file mode 100644 index 8a1d20f9..00000000 --- a/src/gwproto/gs/gs_dispatch.py +++ /dev/null @@ -1,14 +0,0 @@ -"""GridWorks serial message protocol GsPwr100 with MpAlias d""" - -from gwproto.errors import SchemaError -from gwproto.gs.gs_dispatch_base import GsDispatchBase - - -class GsDispatch(GsDispatchBase): - def check_for_errors(self) -> None: - errors = self.derived_errors() + self.hand_coded_errors() - if len(errors) > 0: - raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - - def hand_coded_errors(self): # noqa - return [] diff --git a/src/gwproto/gs/gs_dispatch_base.py b/src/gwproto/gs/gs_dispatch_base.py deleted file mode 100644 index 70739dc3..00000000 --- a/src/gwproto/gs/gs_dispatch_base.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Base for GridWorks gwproto gs.dispatch.100 with TypeName d""" - -import struct -from typing import List, NamedTuple - -from gwproto import property_format - - -class GsDispatchBase(NamedTuple): - RelayState: int - TypeName: str = "d" - - def as_type(self) -> bytes: - return struct.pack(" List[str]: - errors = [] - if self.TypeName != "d": - errors.append(f"Payload requires TypeName of d, not {self.TypeName}.") - if not isinstance(self.RelayState, int): - errors.append(f"Name {self.RelayState} must have type int") - if not property_format.is_bit(self.RelayState): - errors.append(f"RelayState {self.RelayState} must be 0 or 1") - return errors diff --git a/src/gwproto/gs/gs_dispatch_maker.py b/src/gwproto/gs/gs_dispatch_maker.py deleted file mode 100644 index 4b08b18a..00000000 --- a/src/gwproto/gs/gs_dispatch_maker.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Makes GridWorksSerial protocol GsDispatch with MpAlias d""" - -import struct - -from gwproto.gs.gs_dispatch import GsDispatch - - -class GsDispatch_Maker: - type_name = "d" - - def __init__(self, relay_state: int) -> None: - tpl = GsDispatch(RelayState=relay_state) # noqa: ALL - tpl.check_for_errors() - self.tuple = tpl - - @classmethod - def tuple_to_type(cls, tpl: GsDispatch) -> bytes: - tpl.check_for_errors() - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, b: bytes) -> GsDispatch: - (relay_state,) = struct.unpack(" None: - errors = self.derived_errors() + self.hand_coded_errors() - if len(errors) > 0: - raise SchemaError(f" Errors making making gs.pwr.100 for {self}: {errors}") - - def hand_coded_errors(self): # noqa - return [] diff --git a/src/gwproto/gs/gs_pwr_base.py b/src/gwproto/gs/gs_pwr_base.py deleted file mode 100644 index 06b09317..00000000 --- a/src/gwproto/gs/gs_pwr_base.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Base for GridWorks gwproto gs.pwr.100 with TypeName p""" - -import struct -from typing import List, NamedTuple - -from gwproto import property_format - - -class GsPwrBase(NamedTuple): - Power: int - TypeName: str = "p" - - def as_type(self) -> bytes: - return struct.pack(" List[str]: - errors = [] - if self.TypeName != "p": - errors.append(f"Payload requires TypeName of p, not {self.TypeName}.") - if not isinstance(self.Power, int): - errors.append(f"Name {self.Power} must have type int") - if not property_format.is_short_integer(self.Power): - errors.append( - f"Power {self.Power} does not work. Short format requires (-32767 -1) <= number <= 32767" - ) - return errors diff --git a/src/gwproto/gs/gs_pwr_maker.py b/src/gwproto/gs/gs_pwr_maker.py deleted file mode 100644 index d69953e4..00000000 --- a/src/gwproto/gs/gs_pwr_maker.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Makes GridWorksSerial protocol gs.pwr.100 with MpAlias p""" - -import struct - -from gwproto.gs.gs_pwr import GsPwr - - -class GsPwr_Maker: - type_name = "p" - - def __init__(self, power: int) -> None: - tpl = GsPwr(Power=power) # noqa: ALL - tpl.check_for_errors() - self.tuple = tpl - - @classmethod - def tuple_to_type(cls, tpl: GsPwr) -> bytes: - tpl.check_for_errors() - return tpl.as_type() - - @classmethod - def type_to_tuple(cls, b: bytes) -> GsPwr: - (power,) = struct.unpack(" bool: +def check_is_log_style_date_with_millis(v: str) -> None: + """Checks LogStyleDateWithMillis format + + LogStyleDateWithMillis format: YYYY-MM-DDTHH:mm:ss.SSS + + Args: + v (str): the candidate + + Raises: + ValueError: if v is not LogStyleDateWithMillis format. + In particular the milliseconds must have exactly 3 digits. + """ + correct_millisecond_part_length = 3 try: - struct.pack("h", candidate) - except: # noqa - return False - return True + datetime.fromisoformat(v) + except ValueError as e: + raise ValueError(f"{v} is not in LogStyleDateWithMillis format") from e + # The python fromisoformat allows for either 3 digits (milli) or 6 (micro) + # after the final period. Make sure its 3 + milliseconds_part = v.split(".")[1] + if len(milliseconds_part) != correct_millisecond_part_length: + raise ValueError( + f"{v} is not in LogStyleDateWithMillis format." + " Milliseconds must have exactly 3 digits" + ) + + +def is_handle_name(v: str) -> None: + """ + HandleName format: words separated by periods, where the worlds are lowercase + alphanumeric plus hyphens + """ + try: + x = v.split(".") + except Exception as e: + raise ValueError(f"Failed to seperate <{v}> into words with split'.'") from e + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError( + f"Most significant word of <{v}> must start wif64th alphabet char." + ) + for word in x: + for char in word: + if not (char.isalnum() or char == "-"): + raise ValueError( + f"words of <{v}> split by by '.' must be alphanumeric or hyphen." + ) + if not v.islower(): + raise ValueError(f" <{v}> must be lowercase.") + return v -def check_is_hex_char(v: str) -> str: +def is_hex_char(v: str) -> str: """Checks HexChar format HexChar format: single-char string in '0123456789abcdefABCDEF' @@ -38,7 +83,13 @@ def check_is_hex_char(v: str) -> str: return v -def check_is_left_right_dot(candidate: str) -> str: +def is_int(v: int) -> int: + if not isinstance(v, int): + raise TypeError("Not an integer!") + return v + + +def is_left_right_dot(candidate: str) -> str: """Lowercase AlphanumericStrings separated by dots (i.e. periods), with most significant word to the left. I.e. `d1.ne` is the child of `d1`. Checking the format cannot verify the significance of words. All @@ -69,39 +120,108 @@ def check_is_left_right_dot(candidate: str) -> str: return candidate -def str_is_valid_uuid4(v: str) -> str: +MAC_REGEX = re.compile("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") + + +def has_mac_address_format(mac_str: str) -> bool: + return bool(MAC_REGEX.match(mac_str.lower())) + + +def is_spaceheat_name(v: str) -> str: + """ + SpaceheatName format: Lowercase alphanumeric words separated by hypens + """ + try: + x = v.split("-") + except Exception as e: + raise ValueError( + f"<{v}>: Fails SpaceheatName format! Failed to seperate into words with split'-'" + ) from e + first_word = x[0] + first_char = first_word[0] + if not first_char.isalpha(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! Most significant word must start with alphabet char." + ) + for word in x: + if not word.isalnum(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! words of split by by '-' must be alphanumeric." + ) + if not v.islower(): + raise ValueError( + f"<{v}>: Fails SpaceheatName format! All characters of must be lowercase." + ) + return v + + +def is_uuid4_str(v: str) -> str: v = str(v) try: u = uuid.UUID(v) except Exception as e: - raise ValueError(f"Invalid UUID4: {v} <{e}>") from e + raise ValueError(f"Invalid UUID4: <{v} <{e}>") from e if u.version != 4: raise ValueError(f"{v} is valid uid, but of version {u.version}, not 4") return str(u) -def is_bit(candidate: int) -> bool: - return not candidate not in (0, 1) +def is_world_instance_name_format(candidate: str) -> bool: + try: + words = candidate.split("__") + except: # noqa + return False + if len(words) != 2: + return False + try: + int(words[1]) + except: # noqa + return False + try: + root_g_node_alias_words = words[0].split(".") + except: # noqa + return False + return not len(root_g_node_alias_words) > 1 + + +def check_is_ads1115_i2c_address(v: str) -> None: + """ + Ads1115I2cAddress: ToLower(v) in ['0x48', '0x49', '0x4a', '0x4b']. + One of the 4 allowable I2C addresses for Texas Instrument Ads1115 chips. -def check_is_bit(candidate: int) -> int: - if candidate not in (0, 1): - raise ValueError(f"Candidate must be 0 or 1, Got {candidate}") - return candidate + Raises: + ValueError: if not Ads1115I2cAddress format + """ + if v.lower() not in ["0x48", "0x49", "0x4a", "0x4b"]: + raise ValueError(f"Not Ads1115I2cAddress: <{v}>") -HexChar = Annotated[str, BeforeValidator(check_is_hex_char)] +def check_is_near5(v: str) -> None: + """ + 4.5 <= v <= 5.5 + """ + min_pi_voltage = 4.5 + max_pi_voltage = 5.5 + if v < min_pi_voltage or v > max_pi_voltage: + raise ValueError(f"<{v}> is not between 4.5 and 5.5, not Near5") -LeftRightDotStr = Annotated[str, BeforeValidator(check_is_left_right_dot)] -UUID4Str = Annotated[str, BeforeValidator(str_is_valid_uuid4)] +def is_bit(candidate: int) -> int: + if candidate not in (0, 1): + raise ValueError(f"Candidate must be 0 or 1, Got {candidate}") + return candidate + +Bit = Annotated[int, BeforeValidator(is_bit)] +HandleName = Annotated[str, BeforeValidator(is_handle_name)] +HexChar = Annotated[str, BeforeValidator(is_hex_char)] +LeftRightDotStr = Annotated[str, BeforeValidator(is_left_right_dot)] +SpaceheatName = Annotated[str, BeforeValidator(is_spaceheat_name)] +UUID4Str = Annotated[str, BeforeValidator(is_uuid4_str)] UTCSeconds = Annotated[ int, Field(ge=UTC_2000_01_01_TIMESTAMP, le=UTC_3000_01_01_TIMESTAMP) ] - UTCMilliseconds = Annotated[ int, Field(ge=UTC_2000_01_01_TIMESTAMP * 1000, le=UTC_3000_01_01_TIMESTAMP * 1000) ] - -Bit = Annotated[int, BeforeValidator(check_is_bit)] diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index e5936fa8..85748dc1 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -2,7 +2,7 @@ from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt -from gwproto.types.data_channel import DataChannel +from gwproto.types.data_channel_gt import DataChannelGt from gwproto.types.egauge_io import EgaugeIo from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, @@ -85,7 +85,6 @@ ) from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt -from gwproto.types.ta_data_channels import TaDataChannels from gwproto.types.telemetry_reporting_config import ( TelemetryReportingConfig, ) @@ -96,7 +95,7 @@ __all__ = [ "ComponentAttributeClassGt", "ComponentGt", - "DataChannel", + "DataChannelGt", "EgaugeIo", "EgaugeRegisterConfig", "ElectricMeterCacGt", @@ -135,7 +134,6 @@ "SimpleTempSensorComponentGt", "SnapshotSpaceheat", "SpaceheatNodeGt", - "TaDataChannels", "TelemetryReportingConfig", "TelemetrySnapshotSpaceheat", "cacs", # noqa: F822 diff --git a/src/gwproto/types/data_channel.py b/src/gwproto/types/data_channel.py deleted file mode 100644 index 6acb8d0e..00000000 --- a/src/gwproto/types/data_channel.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Type data.channel, version 000""" - -from typing import Literal - -from pydantic import BaseModel, Field - -from gwproto.enums import TelemetryName -from gwproto.property_format import LeftRightDotStr - - -class DataChannel(BaseModel): - """ - Data Channel. - - A data channel is a concept of some collection of readings that share all characteristics - other than time. - """ - - DisplayName: str = Field( - title="Display Name", - description=( - "This display name is the handle for the data channel. It is meant to be set by the " - "person/people who will be analyzing time series data. It is only expected to be " - "unique within the data channels associated to a specific Terminal Asset." - ), - ) - AboutName: LeftRightDotStr - CapturedByName: LeftRightDotStr - TelemetryName: TelemetryName - TypeName: Literal["data.channel"] = "data.channel" - Version: Literal["000"] = "000" diff --git a/src/gwproto/types/data_channel_gt.py b/src/gwproto/types/data_channel_gt.py new file mode 100644 index 00000000..59d88110 --- /dev/null +++ b/src/gwproto/types/data_channel_gt.py @@ -0,0 +1,39 @@ +"""Type data.channel.gt, version 001""" + +from typing import Literal, Optional + +from pydantic import BaseModel, ConfigDict, model_validator +from typing_extensions import Self + +from gwproto.enums import TelemetryName +from gwproto.property_format import ( + LeftRightDotStr, + SpaceheatName, + UTCSeconds, + UUID4Str, +) + + +class DataChannelGt(BaseModel): + Name: SpaceheatName + DisplayName: str + AboutNodeName: SpaceheatName + CapturedByNodeName: SpaceheatName + TelemetryName: TelemetryName + TerminalAssetAlias: LeftRightDotStr + InPowerMetering: Optional[bool] = None + StartS: Optional[UTCSeconds] = None + Id: UUID4Str + TypeName: Literal["data.channel.gt"] = "data.channel.gt" + Version: Literal["001"] = "001" + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: Power Metering. + If InPowerMetering is true then the TelemetryName must be PowerW + """ + # Implement check for axiom 1" + return self + + model_config = ConfigDict(use_enum_values=True) diff --git a/src/gwproto/types/ta_data_channels.py b/src/gwproto/types/ta_data_channels.py deleted file mode 100644 index 65027a6f..00000000 --- a/src/gwproto/types/ta_data_channels.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Type ta.data.channels, version 000""" - -from typing import Literal - -from pydantic import BaseModel - -from gwproto.property_format import LeftRightDotStr, UTCSeconds, UUID4Str -from gwproto.types.data_channel import DataChannel - - -class TaDataChannels(BaseModel): - TerminalAssetGNodeAlias: LeftRightDotStr - TerminalAssetGNodeId: UUID4Str - TimeUnixS: UTCSeconds - Author: str - Channels: list[DataChannel] - Identifier: UUID4Str - TypeName: Literal["ta.data.channels"] = "ta.data.channels" - Version: Literal["000"] = "000" diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 379fff81..74a2f546 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -74,9 +74,9 @@ def child_to_parent_messages() -> list[MessageCase]: snapshot_event = SnapshotSpaceheatEvent(Src=CHILD, snap=snapshot_spaceheat) return [ - # Gs Pwr + # PowerWatts MessageCase( - "GsPwr", + "PowerWatts", Message(Src=CHILD, MessageType="power.watts", Payload=PowerWatts(Watts=1)), ), # status diff --git a/tests/test_gs_dispatch.py b/tests/test_gs_dispatch.py deleted file mode 100644 index 972aa16f..00000000 --- a/tests/test_gs_dispatch.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest - -from gwproto.errors import SchemaError -from gwproto.messages import GsDispatch_Maker as Maker - - -def test_gs_dispatch() -> None: - gw_tuple = Maker(relay_state=1).tuple - - assert Maker.tuple_to_type(gw_tuple) == b"\x01\x00" - assert Maker.type_to_tuple(b"\x01\x00") == gw_tuple - - with pytest.raises(SchemaError): - Maker(relay_state="hi") - - with pytest.raises(SchemaError): - Maker(relay_state=2) diff --git a/tests/test_gs_pwr.py b/tests/test_gs_pwr.py deleted file mode 100644 index b068cb5b..00000000 --- a/tests/test_gs_pwr.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest - -from gwproto.errors import SchemaError -from gwproto.messages import GsPwr_Maker as Maker - - -def test_gs_pwr() -> None: - gw_tuple = Maker(power=3200).tuple - - assert Maker.tuple_to_type(gw_tuple) == b"\x80\x0c" - assert Maker.type_to_tuple(b"\x80\x0c") == gw_tuple - - with pytest.raises(SchemaError): - Maker(power="hi") - - with pytest.raises(SchemaError): - Maker(power=32768) diff --git a/tests/types/test_data_channel.py b/tests/types/test_data_channel.py deleted file mode 100644 index 1108a0d7..00000000 --- a/tests/types/test_data_channel.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Tests data.channel type, version 000""" - -from gwproto.types import DataChannel - - -def test_data_channel_generated() -> None: - d = { - "DisplayName": "BoostPower", - "AboutName": "a.elt1", - "CapturedByName": "a.m", - "TelemetryName": "PowerW", - "TypeName": "data.channel", - "Version": "000", - } - assert DataChannel.model_validate(d).model_dump() == d diff --git a/tests/types/test_data_channel_gt.py b/tests/types/test_data_channel_gt.py new file mode 100644 index 00000000..c7ea4e25 --- /dev/null +++ b/tests/types/test_data_channel_gt.py @@ -0,0 +1,33 @@ +"""Tests data.channel.gt type, version 001""" + +from gwproto.enums import TelemetryName +from gwproto.types import DataChannelGt + + +def test_data_channel_gt_generated() -> None: + d = { + "Name": "hp-idu-pwr", + "DisplayName": "Hp IDU", + "AboutNodeName": "hp-idu-pwr", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "hw1.isone.me.versant.keene.beech.ta", + "InPowerMetering": True, + "StartS": 1721405699, + "Id": "50cf426b-ff3f-4a30-8415-8d3fba5e0ab7", + "TypeName": "data.channel.gt", + "Version": "001", + } + + d2 = DataChannelGt.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d + + ###################################### + # Enum related + ###################################### + + assert type(d2["TelemetryName"]) is str + + d2 = dict(d, TelemetryName="unknown_enum_thing") + assert DataChannelGt(**d2).TelemetryName == TelemetryName.default() diff --git a/tests/types/test_ta_data_channels.py b/tests/types/test_ta_data_channels.py deleted file mode 100644 index 4ce0855c..00000000 --- a/tests/types/test_ta_data_channels.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests ta.data.channels type, version 000""" - -from gwproto.types import TaDataChannels - - -def test_ta_data_channels_generated() -> None: - d = { - "TerminalAssetGNodeAlias": "hw1.isone.me.versant.keene.oak.ta", - "TerminalAssetGNodeId": "7e152072-c91b-49d2-9ebd-f4fe1b684d06", - "TimeUnixS": 1704142951, - "Author": "Jessica Millar", - "Channels": [ - { - "DisplayName": "BoostPower", - "AboutName": "a.elt1", - "CapturedByName": "a.m", - "TelemetryName": "PowerW", - "TypeName": "data.channel", - "Version": "000", - } - ], - "Identifier": "ba6558d8-2fe8-4174-ac16-c36f84367c50", - "TypeName": "ta.data.channels", - "Version": "000", - } - assert TaDataChannels.model_validate(d).model_dump() == d From 4b8f3f2c12459c03b2aeb00a2e3191717dba449a Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 18:32:07 -0400 Subject: [PATCH 125/168] remove GtTelemetry and GtDispatchBoolean etc --- src/gwproto/messages/__init__.py | 5 --- src/gwproto/types/__init__.py | 16 +-------- src/gwproto/types/data_channel_gt.py | 2 +- src/gwproto/types/gt_dispatch_boolean.py | 33 ------------------- .../types/gt_dispatch_boolean_local.py | 32 ------------------ .../types/gt_driver_booleanactuator_cmd.py | 24 -------------- src/gwproto/types/gt_telemetry.py | 20 ----------- tests/test_decoders.py | 16 --------- tests/types/test_gt_dispatch_boolean.py | 18 ---------- tests/types/test_gt_dispatch_boolean_local.py | 16 --------- .../test_gt_driver_booleanactuator_cmd.py | 15 --------- .../test_gt_sh_booleanactuator_cmd_status.py | 14 -------- tests/types/test_gt_telemetry.py | 15 --------- 13 files changed, 2 insertions(+), 224 deletions(-) delete mode 100644 src/gwproto/types/gt_dispatch_boolean.py delete mode 100644 src/gwproto/types/gt_dispatch_boolean_local.py delete mode 100644 src/gwproto/types/gt_driver_booleanactuator_cmd.py delete mode 100644 src/gwproto/types/gt_telemetry.py delete mode 100644 tests/types/test_gt_dispatch_boolean.py delete mode 100644 tests/types/test_gt_dispatch_boolean_local.py delete mode 100644 tests/types/test_gt_driver_booleanactuator_cmd.py delete mode 100644 tests/types/test_gt_sh_booleanactuator_cmd_status.py delete mode 100644 tests/types/test_gt_telemetry.py diff --git a/src/gwproto/messages/__init__.py b/src/gwproto/messages/__init__.py index a462cecf..177518c1 100644 --- a/src/gwproto/messages/__init__.py +++ b/src/gwproto/messages/__init__.py @@ -12,17 +12,12 @@ "EventBase", "EventMessage", "EventT", - "GtDispatchBoolean", - "GtDispatchBooleanLocal", - "GtDriverBooleanactuatorCmd", - "GtShBooleanactuatorCmdStatus", "GtShCliAtnCmd", "GtShMultipurposeTelemetryStatus", "GtShSimpleTelemetryStatus", "GtShStatus", "GtShStatusEvent", "GtShTelemetryFromMultipurposeSensor", - "GtTelemetry", "MQTTConnectEvent", "MQTTConnectFailedEvent", "MQTTDisconnectEvent", diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 85748dc1..c3bad864 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -17,16 +17,7 @@ from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, ) -from gwproto.types.gt_dispatch_boolean import GtDispatchBoolean -from gwproto.types.gt_dispatch_boolean_local import ( - GtDispatchBooleanLocal, -) -from gwproto.types.gt_driver_booleanactuator_cmd import ( - GtDriverBooleanactuatorCmd, -) -from gwproto.types.gt_sh_booleanactuator_cmd_status import ( - GtShBooleanactuatorCmdStatus, -) +from gwproto.types.gt_sh_booleanactuator_cmd_status import GtShBooleanactuatorCmdStatus from gwproto.types.gt_sh_cli_atn_cmd import GtShCliAtnCmd from gwproto.types.gt_sh_multipurpose_telemetry_status import ( GtShMultipurposeTelemetryStatus, @@ -38,7 +29,6 @@ from gwproto.types.gt_sh_telemetry_from_multipurpose_sensor import ( GtShTelemetryFromMultipurposeSensor, ) -from gwproto.types.gt_telemetry import GtTelemetry from gwproto.types.heartbeat_b import HeartbeatB from gwproto.types.hubitat_cac_gt import HubitatCacGt from gwproto.types.hubitat_component_gt import ( @@ -102,16 +92,12 @@ "ElectricMeterComponentGt", "FibaroSmartImplantCacGt", "FibaroSmartImplantComponentGt", - "GtDispatchBoolean", - "GtDispatchBooleanLocal", - "GtDriverBooleanactuatorCmd", "GtShBooleanactuatorCmdStatus", "GtShCliAtnCmd", "GtShMultipurposeTelemetryStatus", "GtShSimpleTelemetryStatus", "GtShStatus", "GtShTelemetryFromMultipurposeSensor", - "GtTelemetry", "HeartbeatB", "HubitatCacGt", "HubitatComponentGt", diff --git a/src/gwproto/types/data_channel_gt.py b/src/gwproto/types/data_channel_gt.py index 59d88110..8395a5df 100644 --- a/src/gwproto/types/data_channel_gt.py +++ b/src/gwproto/types/data_channel_gt.py @@ -35,5 +35,5 @@ def check_axiom_1(self) -> Self: """ # Implement check for axiom 1" return self - + model_config = ConfigDict(use_enum_values=True) diff --git a/src/gwproto/types/gt_dispatch_boolean.py b/src/gwproto/types/gt_dispatch_boolean.py deleted file mode 100644 index f3f614a2..00000000 --- a/src/gwproto/types/gt_dispatch_boolean.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Type gt.dispatch.boolean, version 110""" - -from typing import Literal - -from pydantic import BaseModel, Field - -from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds, UUID4Str - - -class GtDispatchBoolean(BaseModel): - """ - GridWorks Type Boolean Dispatch. - - Boolean dispatch command designed to be sent from an AtomicTNode to a SCADA. - """ - - AboutNodeName: LeftRightDotStr - ToGNodeAlias: LeftRightDotStr - FromGNodeAlias: LeftRightDotStr - FromGNodeInstanceId: UUID4Str - RelayState: Bit = Field( - title="Relay State (False or True)", - description=( - "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " - "the relay is CLOSED (on). Note that `False` means the relay is open whether or not the " - "relay is normally open or normally closed (For a normally open relay, the relay " - "is ENERGIZED when it is in state `False` and DE-ENERGIZED when it is in state `True`.)" - "[More info](https://gridworks.readthedocs.io/en/latest/relay-state.html)" - ), - ) - SendTimeUnixMs: UTCMilliseconds - TypeName: Literal["gt.dispatch.boolean"] = "gt.dispatch.boolean" - Version: Literal["110"] = "110" diff --git a/src/gwproto/types/gt_dispatch_boolean_local.py b/src/gwproto/types/gt_dispatch_boolean_local.py deleted file mode 100644 index b9713549..00000000 --- a/src/gwproto/types/gt_dispatch_boolean_local.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Type gt.dispatch.boolean.local, version 110""" - -from typing import Literal - -from pydantic import BaseModel, Field - -from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds - - -class GtDispatchBooleanLocal(BaseModel): - """ - Dispatch message sent locally by SCADA HomeAlone actor. - - By Locally, this means sent without access to Internet. The HomeAlone actor must reside - within the Local Area Network of the SCADA - typically it should reside on the same hardware. - """ - - AboutNodeName: LeftRightDotStr - FromNodeName: LeftRightDotStr - RelayState: Bit = Field( - title="Relay State (False or True)", - description=( - "A Relay State of `False` indicates the relay is OPEN (off). A Relay State of `True` indicates " - "the relay is CLOSED (on). Note that `False` means the relay is open whether or not the " - "relay is normally open or normally closed (For a normally open relay, the relay " - "is ENERGIZED when it is in state `False` and DE-ENERGIZED when it is in state `True`.)" - "[More info](https://gridworks.readthedocs.io/en/latest/relay-state.html)" - ), - ) - SendTimeUnixMs: UTCMilliseconds - TypeName: Literal["gt.dispatch.boolean.local"] = "gt.dispatch.boolean.local" - Version: Literal["110"] = "110" diff --git a/src/gwproto/types/gt_driver_booleanactuator_cmd.py b/src/gwproto/types/gt_driver_booleanactuator_cmd.py deleted file mode 100644 index 9ec7559a..00000000 --- a/src/gwproto/types/gt_driver_booleanactuator_cmd.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Type gt.driver.booleanactuator.cmd, version 100""" - -from typing import Literal - -from pydantic import BaseModel - -from gwproto.property_format import Bit, LeftRightDotStr, UTCMilliseconds - - -class GtDriverBooleanactuatorCmd(BaseModel): - """ - Boolean Actuator Driver Command. - - The boolean actuator actor reports when it has sent an actuation command to its driver so - that the SCADA can add this to information to be sent up to the AtomicTNode. - - [More info](https://gridworks.readthedocs.io/en/latest/relay-state.html) - """ - - RelayState: Bit - ShNodeAlias: LeftRightDotStr - CommandTimeUnixMs: UTCMilliseconds - TypeName: Literal["gt.driver.booleanactuator.cmd"] = "gt.driver.booleanactuator.cmd" - Version: Literal["100"] = "100" diff --git a/src/gwproto/types/gt_telemetry.py b/src/gwproto/types/gt_telemetry.py deleted file mode 100644 index f07ed588..00000000 --- a/src/gwproto/types/gt_telemetry.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Type gt.telemetry, version 110""" - -from typing import Literal - -from pydantic import BaseModel - -from gwproto.enums import TelemetryName -from gwproto.property_format import UTCMilliseconds - - -class GtTelemetry(BaseModel): - ScadaReadTimeUnixMs: UTCMilliseconds - Value: int - Name: TelemetryName - Exponent: int - TypeName: Literal["gt.telemetry"] = "gt.telemetry" - Version: Literal["110"] = "110" - - def __hash__(self) -> int: - return hash((type(self), *self.__dict__.values())) diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 74a2f546..0eb2db90 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -1,5 +1,4 @@ import json -import time import uuid from pathlib import Path @@ -24,7 +23,6 @@ StartupEvent, ) from gwproto.types import ( - GtDispatchBoolean, GtShCliAtnCmd, GtShStatus, PowerWatts, @@ -186,14 +184,6 @@ def parent_to_child_messages() -> list[MessageCase]: FromGNodeId=str(uuid.uuid4()), SendSnapshot=True, ) - set_relay = GtDispatchBoolean( - AboutNodeName="a.b.c", - ToGNodeAlias="a.b.c", - FromGNodeAlias="a.b.c", - FromGNodeInstanceId=str(uuid.uuid4()), - RelayState=True, - SendTimeUnixMs=int(time.time() * 1000), - ) return [ # misc messages MessageCase("ping", PingMessage(Src=PARENT)), @@ -204,12 +194,6 @@ def parent_to_child_messages() -> list[MessageCase]: None, snapshot_request, ), - MessageCase( - "set-relay", - Message(Src=PARENT, Payload=set_relay), - None, - set_relay, - ), ] diff --git a/tests/types/test_gt_dispatch_boolean.py b/tests/types/test_gt_dispatch_boolean.py deleted file mode 100644 index 5bdfec99..00000000 --- a/tests/types/test_gt_dispatch_boolean.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Tests gt.dispatch.boolean type, version 110""" - -from gwproto.types import GtDispatchBoolean - - -def test_gt_dispatch_boolean_generated() -> None: - d = { - "AboutNodeName": "a.elt1.relay", - "ToGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", - "FromGNodeAlias": "dwtest.isone.ct.newhaven.orange1", - "FromGNodeInstanceId": "e7f7d6cc-08b0-4b36-bbbb-0a1f8447fd32", - "RelayState": 0, - "SendTimeUnixMs": 1657024737661, - "TypeName": "gt.dispatch.boolean", - "Version": "110", - } - payload = GtDispatchBoolean.model_validate(d) - assert payload.model_dump() == d diff --git a/tests/types/test_gt_dispatch_boolean_local.py b/tests/types/test_gt_dispatch_boolean_local.py deleted file mode 100644 index e241d403..00000000 --- a/tests/types/test_gt_dispatch_boolean_local.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Tests gt.dispatch.boolean.local type, version 110""" - -from gwproto.types import GtDispatchBooleanLocal - - -def test_gt_dispatch_boolean_local_generated() -> None: - d = { - "RelayState": 1, - "AboutNodeName": "a.elt1.relay", - "FromNodeName": "a.s", - "SendTimeUnixMs": 1657025211851, - "TypeName": "gt.dispatch.boolean.local", - "Version": "110", - } - payload = GtDispatchBooleanLocal.model_validate(d) - assert payload.model_dump() == d diff --git a/tests/types/test_gt_driver_booleanactuator_cmd.py b/tests/types/test_gt_driver_booleanactuator_cmd.py deleted file mode 100644 index aba9f380..00000000 --- a/tests/types/test_gt_driver_booleanactuator_cmd.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Tests gt.driver.booleanactuator.cmd type, version 100""" - -from gwproto.types import GtDriverBooleanactuatorCmd - - -def test_gt_driver_booleanactuator_cmd_generated() -> None: - d = { - "RelayState": 0, - "ShNodeAlias": "a.elt1.relay", - "CommandTimeUnixMs": 1656869326637, - "TypeName": "gt.driver.booleanactuator.cmd", - "Version": "100", - } - payload = GtDriverBooleanactuatorCmd.model_validate(d) - assert payload.model_dump() == d diff --git a/tests/types/test_gt_sh_booleanactuator_cmd_status.py b/tests/types/test_gt_sh_booleanactuator_cmd_status.py deleted file mode 100644 index e4f98091..00000000 --- a/tests/types/test_gt_sh_booleanactuator_cmd_status.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Tests gt.sh.booleanactuator.cmd.status type, version 100""" - -from gwproto.types import GtShBooleanactuatorCmdStatus - - -def test_gt_sh_booleanactuator_cmd_status_generated() -> None: - d = { - "ShNodeAlias": "a.elt1.relay", - "RelayStateCommandList": [0], - "CommandTimeUnixMsList": [1656443704800], - "TypeName": "gt.sh.booleanactuator.cmd.status", - "Version": "100", - } - assert GtShBooleanactuatorCmdStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_telemetry.py b/tests/types/test_gt_telemetry.py deleted file mode 100644 index 38549d1e..00000000 --- a/tests/types/test_gt_telemetry.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Tests gt.telemetry type, version 110""" - -from gwproto.types import GtTelemetry - - -def test_gt_telemetry_generated() -> None: - d = { - "ScadaReadTimeUnixMs": 1656513094288, - "Value": 63430, - "Name": "WaterTempCTimes1000", - "Exponent": 3, - "TypeName": "gt.telemetry", - "Version": "110", - } - assert GtTelemetry.model_validate(d).model_dump() == d From 24b332a35964801986684f06d9b36b7faf65fde1 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 19:01:26 -0400 Subject: [PATCH 126/168] Remove various cacs and components --- .../json/fibaro-smart-implant-cac-gt.json | 51 ------------ docs/asls/json/gt-dispatch-boolean-local.json | 64 --------------- docs/asls/json/gt-dispatch-boolean.json | 79 ------------------- .../json/gt-driver-booleanactuator-cmd.json | 56 ------------- docs/asls/json/hubitat-cac-gt.json | 39 --------- docs/asls/json/hubitat-poller-cac-gt.json | 37 --------- docs/asls/json/hubitat-tank-cac-gt.json | 39 --------- docs/asls/json/pipe-flow-sensor-cac-gt.json | 50 ------------ .../json/pipe-flow-sensor-component-gt.json | 63 --------------- docs/asls/json/relay-cac-gt.json | 49 ------------ docs/asls/json/relay-component-gt.json | 59 -------------- docs/asls/json/rest-poller-cac-gt.json | 31 -------- docs/asls/json/simple-temp-sensor-cac-gt.json | 70 ---------------- .../json/simple-temp-sensor-component-gt.json | 54 ------------- docs/sdk-types.rst | 17 +--- docs/types/fibaro-smart-implant-cac-gt.rst | 31 -------- docs/types/hubitat-cac-gt.rst | 24 ------ docs/types/hubitat-poller-cac-gt.rst | 27 ------- docs/types/hubitat-tank-cac-gt.rst | 24 ------ docs/types/pipe-flow-sensor-cac-gt.rst | 34 -------- docs/types/pipe-flow-sensor-component-gt.rst | 44 ----------- docs/types/relay-cac-gt.rst | 34 -------- docs/types/relay-component-gt.rst | 41 ---------- docs/types/rest-poller-cac-gt.rst | 24 ------ docs/types/simple-temp-sensor-cac-gt.rst | 46 ----------- .../types/simple-temp-sensor-component-gt.rst | 38 --------- .../data_classes/components/__init__.py | 10 --- .../fibaro_smart_implant_component.py | 7 +- .../components/hubitat_component.py | 7 +- .../components/hubitat_poller_component.py | 9 ++- .../components/hubitat_tank_component.py | 9 ++- .../components/pipe_flow_sensor_component.py | 9 --- .../components/relay_component.py | 7 -- .../components/rest_poller_component.py | 5 +- .../simple_temp_sensor_component.py | 9 --- .../components/web_server_component.py | 4 +- src/gwproto/data_classes/hardware_layout.py | 3 - src/gwproto/types/__init__.py | 34 -------- src/gwproto/types/cacs.py | 18 ----- src/gwproto/types/components.py | 6 -- .../types/fibaro_smart_implant_cac_gt.py | 11 --- src/gwproto/types/hubitat_cac_gt.py | 7 -- src/gwproto/types/hubitat_poller_cac_gt.py | 7 -- src/gwproto/types/hubitat_tank_cac_gt.py | 7 -- src/gwproto/types/pipe_flow_sensor_cac_gt.py | 10 --- .../types/pipe_flow_sensor_component_gt.py | 12 --- src/gwproto/types/relay_cac_gt.py | 10 --- src/gwproto/types/relay_component_gt.py | 11 --- src/gwproto/types/rest_poller_cac_gt.py | 7 -- .../types/simple_temp_sensor_cac_gt.py | 15 ---- .../types/simple_temp_sensor_component_gt.py | 12 --- src/gwproto/types/web_server_cac_gt.py | 7 -- 52 files changed, 27 insertions(+), 1351 deletions(-) delete mode 100644 docs/asls/json/fibaro-smart-implant-cac-gt.json delete mode 100644 docs/asls/json/gt-dispatch-boolean-local.json delete mode 100644 docs/asls/json/gt-dispatch-boolean.json delete mode 100644 docs/asls/json/gt-driver-booleanactuator-cmd.json delete mode 100644 docs/asls/json/hubitat-cac-gt.json delete mode 100644 docs/asls/json/hubitat-poller-cac-gt.json delete mode 100644 docs/asls/json/hubitat-tank-cac-gt.json delete mode 100644 docs/asls/json/pipe-flow-sensor-cac-gt.json delete mode 100644 docs/asls/json/pipe-flow-sensor-component-gt.json delete mode 100644 docs/asls/json/relay-cac-gt.json delete mode 100644 docs/asls/json/relay-component-gt.json delete mode 100644 docs/asls/json/rest-poller-cac-gt.json delete mode 100644 docs/asls/json/simple-temp-sensor-cac-gt.json delete mode 100644 docs/asls/json/simple-temp-sensor-component-gt.json delete mode 100644 docs/types/fibaro-smart-implant-cac-gt.rst delete mode 100644 docs/types/hubitat-cac-gt.rst delete mode 100644 docs/types/hubitat-poller-cac-gt.rst delete mode 100644 docs/types/hubitat-tank-cac-gt.rst delete mode 100644 docs/types/pipe-flow-sensor-cac-gt.rst delete mode 100644 docs/types/pipe-flow-sensor-component-gt.rst delete mode 100644 docs/types/relay-cac-gt.rst delete mode 100644 docs/types/relay-component-gt.rst delete mode 100644 docs/types/rest-poller-cac-gt.rst delete mode 100644 docs/types/simple-temp-sensor-cac-gt.rst delete mode 100644 docs/types/simple-temp-sensor-component-gt.rst delete mode 100644 src/gwproto/data_classes/components/pipe_flow_sensor_component.py delete mode 100644 src/gwproto/data_classes/components/relay_component.py delete mode 100644 src/gwproto/data_classes/components/simple_temp_sensor_component.py delete mode 100644 src/gwproto/types/fibaro_smart_implant_cac_gt.py delete mode 100644 src/gwproto/types/hubitat_cac_gt.py delete mode 100644 src/gwproto/types/hubitat_poller_cac_gt.py delete mode 100644 src/gwproto/types/hubitat_tank_cac_gt.py delete mode 100644 src/gwproto/types/pipe_flow_sensor_cac_gt.py delete mode 100644 src/gwproto/types/pipe_flow_sensor_component_gt.py delete mode 100644 src/gwproto/types/relay_cac_gt.py delete mode 100644 src/gwproto/types/relay_component_gt.py delete mode 100644 src/gwproto/types/rest_poller_cac_gt.py delete mode 100644 src/gwproto/types/simple_temp_sensor_cac_gt.py delete mode 100644 src/gwproto/types/simple_temp_sensor_component_gt.py delete mode 100644 src/gwproto/types/web_server_cac_gt.py diff --git a/docs/asls/json/fibaro-smart-implant-cac-gt.json b/docs/asls/json/fibaro-smart-implant-cac-gt.json deleted file mode 100644 index 11d895fa..00000000 --- a/docs/asls/json/fibaro-smart-implant-cac-gt.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "fibaro.smart.implant.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Fibaro Make/Model. A small IoT Z-Wave device with two analog sensors, two digital outputs and a 1-wire temp sensor.", - "url": "https://www.fibaro.com/us/products/smart-implant/", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "Model": { - "type": "string", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: FGBS-222 v5.2", - "required": false - }, - "TypeName": { - "type": "string", - "value": "fibaro.smart.implant.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentAttributeClassId": "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", - "DisplayName": "Fibaro SmartImplant FGBS-222", - "Model": "FGBS-222 v5.2", - "TypeName": "fibaro.smart.implant.cac.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/gt-dispatch-boolean-local.json b/docs/asls/json/gt-dispatch-boolean-local.json deleted file mode 100644 index fdb75714..00000000 --- a/docs/asls/json/gt-dispatch-boolean-local.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.dispatch.boolean.local", - "version": "110", - "owner": "gridworks@gridworks-consulting.com", - "description": "Dispatch message sent locally by SCADA HomeAlone actor. By Locally, this means sent without access to Internet. The HomeAlone actor must reside within the Local Area Network of the SCADA - typically it should reside on the same hardware.", - "properties": { - "RelayState": { - "type": "integer", - "format": "Bit", - "title": "Relay State (0 or 1)", - "description": "A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates the relay is CLOSED (on). Note that `0` means the relay is open whether or not the relay is normally open or normally closed (For a normally open relay, the relay is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.)", - "required": true - }, - "AboutNodeName": { - "type": "string", - "format": "LeftRightDot", - "title": "About Node Name", - "description": "The boolean actuator Spaceheat Node getting turned on or off.", - "required": true - }, - "FromNodeName": { - "type": "string", - "format": "LeftRightDot", - "title": "From Node Name", - "description": "The Spaceheat Node sending the command.", - "required": true - }, - "SendTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "Send Time in Unix Milliseconds", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.dispatch.boolean.local", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "110", - "required": true - } - }, - "formats": { - "Bit": { - "type": "string", - "description": "The value must be the integer 0 or the integer 1. Will not attempt to first interpret as an integer. For example, 1.3 will not be interpreted as 1 but will raise an error.", - "example": "" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-dispatch-boolean.json b/docs/asls/json/gt-dispatch-boolean.json deleted file mode 100644 index 3013d9cd..00000000 --- a/docs/asls/json/gt-dispatch-boolean.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.dispatch.boolean", - "version": "110", - "owner": "gridworks@gridworks-consulting.com", - "description": "GridWorks Type Boolean Dispatch. Boolean dispatch command designed to be sent from an AtomicTNode to a SCADA.", - "properties": { - "AboutNodeName": { - "type": "string", - "format": "LeftRightDot", - "title": "The Spaceheat Node getting dispatched", - "required": true - }, - "ToGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "GNodeAlias of the SCADA", - "required": true - }, - "FromGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "GNodeAlias of AtomicTNode", - "required": true - }, - "FromGNodeInstanceId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "GNodeInstance of the AtomicTNode", - "required": true - }, - "RelayState": { - "type": "integer", - "format": "Bit", - "title": "Relay State (0 or 1)", - "description": "A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates the relay is CLOSED (on). Note that `0` means the relay is open whether or not the relay is normally open or normally closed (For a normally open relay, the relay is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.)", - "required": true - }, - "SendTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "Time the AtomicTNode sends the dispatch, by its clock", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.dispatch.boolean", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "110", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "Bit": { - "type": "string", - "description": "The value must be the integer 0 or the integer 1. Will not attempt to first interpret as an integer. For example, 1.3 will not be interpreted as 1 but will raise an error.", - "example": "" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-driver-booleanactuator-cmd.json b/docs/asls/json/gt-driver-booleanactuator-cmd.json deleted file mode 100644 index 4d27ccb7..00000000 --- a/docs/asls/json/gt-driver-booleanactuator-cmd.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.driver.booleanactuator.cmd", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Boolean Actuator Driver Command. The boolean actuator actor reports when it has sent an actuation command to its driver so that the SCADA can add this to information to be sent up to the AtomicTNode.", - "url": "https://gridworks.readthedocs.io/en/latest/relay-state.html", - "properties": { - "RelayState": { - "type": "integer", - "format": "Bit", - "title": "", - "required": true - }, - "ShNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "required": true - }, - "CommandTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.driver.booleanactuator.cmd", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "formats": { - "Bit": { - "type": "string", - "description": "The value must be the integer 0 or the integer 1. Will not attempt to first interpret as an integer. For example, 1.3 will not be interpreted as 1 but will raise an error.", - "example": "" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/hubitat-cac-gt.json b/docs/asls/json/hubitat-cac-gt.json deleted file mode 100644 index 30f248e6..00000000 --- a/docs/asls/json/hubitat-cac-gt.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Component Attribute Class (GridWorks Type). Hubitat is a company that makes IoT hubs. This is a type for MakeModels made by Hubitat.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/iot-hubs.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Hubitat Elevation C-7", - "required": false - }, - "TypeName": { - "type": "string", - "value": "hubitat.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentAttributeClassId": "62528da5-b510-4ac2-82c1-3782842eae07", - "DisplayName": "Hubitat Elevation C-7", - "TypeName": "hubitat.cac.gt", - "Version": "000" - } -} diff --git a/docs/asls/json/hubitat-poller-cac-gt.json b/docs/asls/json/hubitat-poller-cac-gt.json deleted file mode 100644 index 1213ea1a..00000000 --- a/docs/asls/json/hubitat-poller-cac-gt.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.poller.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Poller Cac (GridWorks Type). A class of devices - like a Honeywell Z-Wave T6 Thermostat - that can be polled through a Hubitat IoT hub.", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Honeywell T6 ZWave Thermostat", - "required": false - }, - "TypeName": { - "type": "string", - "value": "hubitat.poller.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/hubitat-tank-cac-gt.json b/docs/asls/json/hubitat-tank-cac-gt.json deleted file mode 100644 index 34a9be75..00000000 --- a/docs/asls/json/hubitat-tank-cac-gt.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.tank.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Tank Component Attribute Class (GridWorks Type). A class of MakeModels for GridWorks tank temp sensor modules that use Hubitat hubs as part of the data collection.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/gridworks-tank-module-1.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: GridWorks TankModule1", - "required": false - }, - "TypeName": { - "type": "string", - "value": "hubitat.tank.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentAttributeClassId": "60ac199d-679a-49f7-9142-8ca3e6428a5f", - "DisplayName": "GridWorks TankModule1", - "TypeName": "hubitat.tank.cac.gt", - "Version": "000" - } -} diff --git a/docs/asls/json/pipe-flow-sensor-cac-gt.json b/docs/asls/json/pipe-flow-sensor-cac-gt.json deleted file mode 100644 index ba3f6cd3..00000000 --- a/docs/asls/json/pipe-flow-sensor-cac-gt.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "pipe.flow.sensor.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Pipe Flow Sensor ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Atlas Scientific EZO FLO i2c", - "required": false - }, - "CommsMethod": { - "type": "string", - "required": false - }, - "TypeName": { - "type": "string", - "value": "pipe.flow.sensor.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/pipe-flow-sensor-component-gt.json b/docs/asls/json/pipe-flow-sensor-component-gt.json deleted file mode 100644 index 1d9e0928..00000000 --- a/docs/asls/json/pipe-flow-sensor-component-gt.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "pipe.flow.sensor.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Pipe Flow Sensor Components. Designed for Pipe Flow Sensors. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a PipeFlowSensor, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for PipeFlowSensorCac object articulated by the pipe.flow.sensor.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "I2cAddress": { - "type": "integer", - "required": true - }, - "ConversionFactor": { - "type": "number", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Pipe Flow Meter Component ", - "required": false - }, - "HwUid": { - "type": "string", - "required": false - }, - "ExpectedMaxGpmTimes100": { - "type": "integer", - "required": false - }, - "TypeName": { - "type": "string", - "value": "pipe.flow.sensor.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/relay-cac-gt.json b/docs/asls/json/relay-cac-gt.json deleted file mode 100644 index bb8fa5cb..00000000 --- a/docs/asls/json/relay-cac-gt.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "relay.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Relay ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "TypicalResponseTimeMs": { - "type": "integer", - "required": true - }, - "TypeName": { - "type": "string", - "value": "relay.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/relay-component-gt.json b/docs/asls/json/relay-component-gt.json deleted file mode 100644 index b27d6edd..00000000 --- a/docs/asls/json/relay-component-gt.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "relay.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Relay Components. Designed for Relays. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a Relay, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for RelayCac object articulated by the relay.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "Gpio": { - "type": "integer", - "required": false - }, - "HwUid": { - "type": "string", - "required": false - }, - "NormallyOpen": { - "type": "boolean", - "description": "Normally open relays default in the open position, meaning that when they're not in use, there is no contact between the circuits. When power is introduced, an electromagnet pulls the first circuit into contact with the second, thereby closing the circuit and allowing power to flow through", - "required": true - }, - "TypeName": { - "type": "string", - "value": "relay.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/rest-poller-cac-gt.json b/docs/asls/json/rest-poller-cac-gt.json deleted file mode 100644 index b5572276..00000000 --- a/docs/asls/json/rest-poller-cac-gt.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "rest.poller.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "REST Poller Cac (GridWorks Type). Type for devices that can be polled by SCADA software via a REST endpoint on the LAN. For example a HoneyWell ZWave thermostat that can queried via a IoT hub like a Hubitat C7.", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "TypeName": { - "type": "string", - "value": "rest.poller.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - } -} diff --git a/docs/asls/json/simple-temp-sensor-cac-gt.json b/docs/asls/json/simple-temp-sensor-cac-gt.json deleted file mode 100644 index 2f466107..00000000 --- a/docs/asls/json/simple-temp-sensor-cac-gt.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "simple.temp.sensor.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Simple Temp Sensor ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "", - "required": true - }, - "TypicalResponseTimeMs": { - "type": "integer", - "required": true - }, - "Exponent": { - "type": "integer", - "description": "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C", - "required": true - }, - "TempUnit": { - "type": "string", - "format": "spaceheat.unit", - "title": "", - "required": true - }, - "TelemetryName": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "CommsMethod": { - "type": "string", - "required": false - }, - "TypeName": { - "type": "string", - "value": "simple.temp.sensor.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/simple-temp-sensor-component-gt.json b/docs/asls/json/simple-temp-sensor-component-gt.json deleted file mode 100644 index 49552724..00000000 --- a/docs/asls/json/simple-temp-sensor-component-gt.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "simple.temp.sensor.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Simple Temp Sensor Components. Designed for simple temp sensors that read only one temp. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a SimpleTempSensor, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for SimpleTempSensorCac object articulated by the simple.temp.sensor.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "HwUid": { - "type": "string", - "required": false - }, - "Channel": { - "type": "integer", - "required": false - }, - "TypeName": { - "type": "string", - "value": "simple.temp.sensor.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/sdk-types.rst b/docs/sdk-types.rst index 824babdb..7198b4b3 100644 --- a/docs/sdk-types.rst +++ b/docs/sdk-types.rst @@ -20,43 +20,28 @@ forth between type instances and Python objects. ComponentAttributeClassGt ComponentGt - DataChannel + DataChannelGt EgaugeIo EgaugeRegisterConfig ElectricMeterCacGt ElectricMeterComponentGt - FibaroSmartImplantCacGt FibaroSmartImplantComponentGt - GtDispatchBoolean - GtDispatchBooleanLocal - GtDriverBooleanactuatorCmd GtShBooleanactuatorCmdStatus GtShCliAtnCmd GtShMultipurposeTelemetryStatus GtShSimpleTelemetryStatus GtShStatus GtShTelemetryFromMultipurposeSensor - GtTelemetry HeartbeatB - HubitatCacGt HubitatComponentGt - HubitatPollerCacGt HubitatPollerComponentGt - HubitatTankCacGt HubitatTankComponentGt MultipurposeSensorCacGt MultipurposeSensorComponentGt - PipeFlowSensorCacGt - PipeFlowSensorComponentGt PowerWatts - RelayCacGt - RelayComponentGt ResistiveHeaterCacGt ResistiveHeaterComponentGt - RestPollerCacGt RestPollerComponentGt - SimpleTempSensorCacGt - SimpleTempSensorComponentGt SnapshotSpaceheat SpaceheatNodeGt TelemetryReportingConfig diff --git a/docs/types/fibaro-smart-implant-cac-gt.rst b/docs/types/fibaro-smart-implant-cac-gt.rst deleted file mode 100644 index 40e20a77..00000000 --- a/docs/types/fibaro-smart-implant-cac-gt.rst +++ /dev/null @@ -1,31 +0,0 @@ -FibaroSmartImplantCacGt -========================== -Python pydantic class corresponding to json type `fibaro.smart.implant.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.FibaroSmartImplantCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**Model**: - - Description: - -**DisplayName**: - - Description: Sample: FGBS-222 v5.2 - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.fibaro_smart_implant_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.FibaroSmartImplantCacGt_Maker - :members: diff --git a/docs/types/hubitat-cac-gt.rst b/docs/types/hubitat-cac-gt.rst deleted file mode 100644 index 4b6ddb01..00000000 --- a/docs/types/hubitat-cac-gt.rst +++ /dev/null @@ -1,24 +0,0 @@ -HubitatCacGt -========================== -Python pydantic class corresponding to json type `hubitat.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: DisplayName. Sample: Hubitat Elevation C-7 - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.HubitatCacGt_Maker - :members: diff --git a/docs/types/hubitat-poller-cac-gt.rst b/docs/types/hubitat-poller-cac-gt.rst deleted file mode 100644 index 37075211..00000000 --- a/docs/types/hubitat-poller-cac-gt.rst +++ /dev/null @@ -1,27 +0,0 @@ -HubitatPollerCacGt -========================== -Python pydantic class corresponding to json type `hubitat.poller.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatPollerCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - -**DisplayName**: - - Description: DisplayName. Sample: Honeywell T6 ZWave Thermostat - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.hubitat_poller_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.HubitatPollerCacGt_Maker - :members: diff --git a/docs/types/hubitat-tank-cac-gt.rst b/docs/types/hubitat-tank-cac-gt.rst deleted file mode 100644 index 1e231159..00000000 --- a/docs/types/hubitat-tank-cac-gt.rst +++ /dev/null @@ -1,24 +0,0 @@ -HubitatTankCacGt -========================== -Python pydantic class corresponding to json type `hubitat.tank.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatTankCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: Sample: GridWorks TankModule1 - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.HubitatTankCacGt_Maker - :members: diff --git a/docs/types/pipe-flow-sensor-cac-gt.rst b/docs/types/pipe-flow-sensor-cac-gt.rst deleted file mode 100644 index c0286738..00000000 --- a/docs/types/pipe-flow-sensor-cac-gt.rst +++ /dev/null @@ -1,34 +0,0 @@ -PipeFlowSensorCacGt -========================== -Python pydantic class corresponding to json type `pipe.flow.sensor.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.PipeFlowSensorCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: - -**DisplayName**: - - Description: Sample: Atlas Scientific EZO FLO i2c - -**CommsMethod**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.pipe_flow_sensor_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.PipeFlowSensorCacGt_Maker - :members: diff --git a/docs/types/pipe-flow-sensor-component-gt.rst b/docs/types/pipe-flow-sensor-component-gt.rst deleted file mode 100644 index 07f80513..00000000 --- a/docs/types/pipe-flow-sensor-component-gt.rst +++ /dev/null @@ -1,44 +0,0 @@ -PipeFlowSensorComponentGt -========================== -Python pydantic class corresponding to json type `pipe.flow.sensor.component.gt`, version `000`. - -.. autoclass:: gwproto.types.PipeFlowSensorComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a PipeFlowSensor, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**I2cAddress**: - - Description: - -**ConversionFactor**: - - Description: - -**DisplayName**: - - Description: Sample: Pipe Flow Meter Component - -**HwUid**: - - Description: Hardware Unique Id. - -**ExpectedMaxGpmTimes100**: - - Description: Expected Max Flow in Gallons per Minute, times 100. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.pipe_flow_sensor_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.PipeFlowSensorComponentGt_Maker - :members: diff --git a/docs/types/relay-cac-gt.rst b/docs/types/relay-cac-gt.rst deleted file mode 100644 index e1d5a91a..00000000 --- a/docs/types/relay-cac-gt.rst +++ /dev/null @@ -1,34 +0,0 @@ -RelayCacGt -========================== -Python pydantic class corresponding to json type `relay.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.RelayCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: - -**DisplayName**: - - Description: - -**TypicalResponseTimeMs**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.relay_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.RelayCacGt_Maker - :members: diff --git a/docs/types/relay-component-gt.rst b/docs/types/relay-component-gt.rst deleted file mode 100644 index c30827e0..00000000 --- a/docs/types/relay-component-gt.rst +++ /dev/null @@ -1,41 +0,0 @@ -RelayComponentGt -========================== -Python pydantic class corresponding to json type `relay.component.gt`, version `000`. - -.. autoclass:: gwproto.types.RelayComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a Relay, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: - -**Gpio**: - - Description: - -**HwUid**: - - Description: Hardware Unique Id. - -**NormallyOpen**: - - Description: Normally Open. Normally open relays default in the open position, meaning that when they're not in use, there is no contact between the circuits. When power is introduced, an electromagnet pulls the first circuit into contact with the second, thereby closing the circuit and allowing power to flow through - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.relay_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.RelayComponentGt_Maker - :members: diff --git a/docs/types/rest-poller-cac-gt.rst b/docs/types/rest-poller-cac-gt.rst deleted file mode 100644 index 01793f28..00000000 --- a/docs/types/rest-poller-cac-gt.rst +++ /dev/null @@ -1,24 +0,0 @@ -RestPollerCacGt -========================== -Python pydantic class corresponding to json type `rest.poller.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.RestPollerCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.RestPollerCacGt_Maker - :members: diff --git a/docs/types/simple-temp-sensor-cac-gt.rst b/docs/types/simple-temp-sensor-cac-gt.rst deleted file mode 100644 index ceb1208c..00000000 --- a/docs/types/simple-temp-sensor-cac-gt.rst +++ /dev/null @@ -1,46 +0,0 @@ -SimpleTempSensorCacGt -========================== -Python pydantic class corresponding to json type `simple.temp.sensor.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.SimpleTempSensorCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: - -**TypicalResponseTimeMs**: - - Description: - -**Exponent**: - - Description: Exponent. Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C - -**TempUnit**: - - Description: - -**TelemetryName**: - - Description: - -**DisplayName**: - - Description: - -**CommsMethod**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.simple_temp_sensor_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.SimpleTempSensorCacGt_Maker - :members: diff --git a/docs/types/simple-temp-sensor-component-gt.rst b/docs/types/simple-temp-sensor-component-gt.rst deleted file mode 100644 index 6c728af4..00000000 --- a/docs/types/simple-temp-sensor-component-gt.rst +++ /dev/null @@ -1,38 +0,0 @@ -SimpleTempSensorComponentGt -========================== -Python pydantic class corresponding to json type `simple.temp.sensor.component.gt`, version `000`. - -.. autoclass:: gwproto.types.SimpleTempSensorComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a SimpleTempSensor, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: - -**HwUid**: - - Description: Hardware Unique Id. - -**Channel**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.simple_temp_sensor_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.SimpleTempSensorComponentGt_Maker - :members: diff --git a/src/gwproto/data_classes/components/__init__.py b/src/gwproto/data_classes/components/__init__.py index 90708092..b1baf8e7 100644 --- a/src/gwproto/data_classes/components/__init__.py +++ b/src/gwproto/data_classes/components/__init__.py @@ -13,17 +13,10 @@ from gwproto.data_classes.components.multipurpose_sensor_component import ( MultipurposeSensorComponent, ) -from gwproto.data_classes.components.pipe_flow_sensor_component import ( - PipeFlowSensorComponent, -) -from gwproto.data_classes.components.relay_component import RelayComponent from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterComponent, ) from gwproto.data_classes.components.rest_poller_component import RESTPollerComponent -from gwproto.data_classes.components.simple_temp_sensor_component import ( - SimpleTempSensorComponent, -) from gwproto.data_classes.components.web_server_component import WebServerComponent __all__ = [ @@ -34,10 +27,7 @@ "HubitatPollerComponent", "HubitatTankComponent", "MultipurposeSensorComponent", - "PipeFlowSensorComponent", "RESTPollerComponent", - "RelayComponent", "ResistiveHeaterComponent", - "SimpleTempSensorComponent", "WebServerComponent", ] diff --git a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py index 3a91e7ea..78366486 100644 --- a/src/gwproto/data_classes/components/fibaro_smart_implant_component.py +++ b/src/gwproto/data_classes/components/fibaro_smart_implant_component.py @@ -1,7 +1,10 @@ from gwproto.data_classes.components.component import Component -from gwproto.types import FibaroSmartImplantCacGt, FibaroSmartImplantComponentGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types.fibaro_smart_implant_component_gt import ( + FibaroSmartImplantComponentGt, +) class FibaroSmartImplantComponent( - Component[FibaroSmartImplantComponentGt, FibaroSmartImplantCacGt] + Component[FibaroSmartImplantComponentGt, ComponentAttributeClassGt] ): ... diff --git a/src/gwproto/data_classes/components/hubitat_component.py b/src/gwproto/data_classes/components/hubitat_component.py index 9ba84d4d..2c590f00 100644 --- a/src/gwproto/data_classes/components/hubitat_component.py +++ b/src/gwproto/data_classes/components/hubitat_component.py @@ -3,13 +3,14 @@ import yarl from gwproto.data_classes.components.component import Component -from gwproto.types import HubitatCacGt, HubitatComponentGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types.hubitat_component_gt import HubitatComponentGt -class HubitatComponent(Component[HubitatComponentGt, HubitatCacGt]): +class HubitatComponent(Component[HubitatComponentGt, ComponentAttributeClassGt]): web_listener_nodes: set[str] - def __init__(self, gt: HubitatComponentGt, cac: HubitatCacGt) -> None: + def __init__(self, gt: HubitatComponentGt, cac: ComponentAttributeClassGt) -> None: super().__init__(gt, cac) self.web_listener_nodes = set() diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index 3ea281f8..bb63e897 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -6,19 +6,22 @@ from gwproto.data_classes.components.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode -from gwproto.types import HubitatPollerCacGt, HubitatPollerComponentGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.hubitat_component_gt import HubitatComponentGt +from gwproto.types.hubitat_poller_component_gt import HubitatPollerComponentGt from gwproto.types.rest_poller_gt import RequestArgs, RESTPollerSettings from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig class HubitatPollerComponent( - Component[HubitatPollerComponentGt, HubitatPollerCacGt], ComponentResolver + Component[HubitatPollerComponentGt, ComponentAttributeClassGt], ComponentResolver ): hubitat_gt: HubitatComponentGt _rest: Optional[RESTPollerSettings] = None - def __init__(self, gt: HubitatPollerComponentGt, cac: HubitatPollerCacGt) -> None: + def __init__( + self, gt: HubitatPollerComponentGt, cac: ComponentAttributeClassGt + ) -> None: super().__init__(gt, cac) self.hubitat_gt = HubitatComponentGt.make_stub(gt.Poller.hubitat_component_id) diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index 40d32186..92debede 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -6,11 +6,12 @@ from gwproto.data_classes.components.component import Component from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode -from gwproto.types import HubitatTankCacGt, HubitatTankComponentGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.hubitat_component_gt import ( HubitatComponentGt, HubitatRESTResolutionSettings, ) +from gwproto.types.hubitat_tank_component_gt import HubitatTankComponentGt from gwproto.types.hubitat_tank_gt import ( FibaroTempSensorSettings, FibaroTempSensorSettingsGt, @@ -19,13 +20,15 @@ class HubitatTankComponent( - Component[HubitatTankComponentGt, HubitatTankCacGt], ComponentResolver + Component[HubitatTankComponentGt, ComponentAttributeClassGt], ComponentResolver ): hubitat: HubitatComponentGt devices_gt: list[FibaroTempSensorSettingsGt] devices: list[FibaroTempSensorSettings] - def __init__(self, gt: HubitatTankComponentGt, cac: HubitatTankCacGt) -> None: + def __init__( + self, gt: HubitatTankComponentGt, cac: ComponentAttributeClassGt + ) -> None: super().__init__(gt, cac) # Create self.hubitat as a proxy containing only the id # of the hubitat; the actual component data will be resolved diff --git a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py b/src/gwproto/data_classes/components/pipe_flow_sensor_component.py deleted file mode 100644 index 8b660b58..00000000 --- a/src/gwproto/data_classes/components/pipe_flow_sensor_component.py +++ /dev/null @@ -1,9 +0,0 @@ -"""PipeFlowSensorComponent definition""" - -from gwproto.data_classes.components.component import Component -from gwproto.types import PipeFlowSensorCacGt, PipeFlowSensorComponentGt - - -class PipeFlowSensorComponent( - Component[PipeFlowSensorComponentGt, PipeFlowSensorCacGt] -): ... diff --git a/src/gwproto/data_classes/components/relay_component.py b/src/gwproto/data_classes/components/relay_component.py deleted file mode 100644 index f88b03e8..00000000 --- a/src/gwproto/data_classes/components/relay_component.py +++ /dev/null @@ -1,7 +0,0 @@ -"""RelayComponent definition""" - -from gwproto.data_classes.components.component import Component -from gwproto.types import RelayCacGt, RelayComponentGt - - -class RelayComponent(Component[RelayComponentGt, RelayCacGt]): ... diff --git a/src/gwproto/data_classes/components/rest_poller_component.py b/src/gwproto/data_classes/components/rest_poller_component.py index 76ffcca7..49694ef1 100644 --- a/src/gwproto/data_classes/components/rest_poller_component.py +++ b/src/gwproto/data_classes/components/rest_poller_component.py @@ -1,9 +1,10 @@ from gwproto.data_classes.components.component import Component -from gwproto.types import RESTPollerCacGt, RESTPollerComponentGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt +from gwproto.types.rest_poller_component_gt import RESTPollerComponentGt from gwproto.types.rest_poller_gt import RESTPollerSettings -class RESTPollerComponent(Component[RESTPollerComponentGt, RESTPollerCacGt]): +class RESTPollerComponent(Component[RESTPollerComponentGt, ComponentAttributeClassGt]): @property def rest(self) -> RESTPollerSettings: return self.gt.Rest diff --git a/src/gwproto/data_classes/components/simple_temp_sensor_component.py b/src/gwproto/data_classes/components/simple_temp_sensor_component.py deleted file mode 100644 index 85379302..00000000 --- a/src/gwproto/data_classes/components/simple_temp_sensor_component.py +++ /dev/null @@ -1,9 +0,0 @@ -"""SimpleTempSensorComponent definition""" - -from gwproto.data_classes.components.component import Component -from gwproto.types import SimpleTempSensorCacGt, SimpleTempSensorComponentGt - - -class SimpleTempSensorComponent( - Component[SimpleTempSensorComponentGt, SimpleTempSensorCacGt] -): ... diff --git a/src/gwproto/data_classes/components/web_server_component.py b/src/gwproto/data_classes/components/web_server_component.py index f733d029..707049b5 100644 --- a/src/gwproto/data_classes/components/web_server_component.py +++ b/src/gwproto/data_classes/components/web_server_component.py @@ -1,10 +1,10 @@ from gwproto.data_classes.components.component import Component -from gwproto.types.web_server_cac_gt import WebServerCacGt +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.web_server_component_gt import WebServerComponentGt from gwproto.types.web_server_gt import WebServerGt -class WebServerComponent(Component[WebServerComponentGt, WebServerCacGt]): +class WebServerComponent(Component[WebServerComponentGt, ComponentAttributeClassGt]): @property def web_server_gt(self) -> WebServerGt: return self.gt.WebServer diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index cc98c3f0..f92af513 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -72,12 +72,9 @@ def load_cacs( cac_decoder = default_cac_decoder cacs: dict[str, ComponentAttributeClassGt] = {} for type_name in [ - "RelayCacs", "ResistiveHeaterCacs", "ElectricMeterCacs", - "PipeFlowSensorCacs", "MultipurposeSensorCacs", - "SimpleTempSensorCacs", "OtherCacs", ]: for cac_dict in layout.get(type_name, ()): diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index c3bad864..ddac62d3 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -11,9 +11,6 @@ ElectricMeterCacGt, ) from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt -from gwproto.types.fibaro_smart_implant_cac_gt import ( - FibaroSmartImplantCacGt, -) from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, ) @@ -30,17 +27,12 @@ GtShTelemetryFromMultipurposeSensor, ) from gwproto.types.heartbeat_b import HeartbeatB -from gwproto.types.hubitat_cac_gt import HubitatCacGt from gwproto.types.hubitat_component_gt import ( HubitatComponentGt, ) -from gwproto.types.hubitat_poller_cac_gt import ( - HubitatPollerCacGt, -) from gwproto.types.hubitat_poller_component_gt import ( HubitatPollerComponentGt, ) -from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt from gwproto.types.hubitat_tank_component_gt import ( HubitatTankComponentGt, ) @@ -48,31 +40,16 @@ MultipurposeSensorCacGt, ) from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt -from gwproto.types.pipe_flow_sensor_cac_gt import ( - PipeFlowSensorCacGt, -) -from gwproto.types.pipe_flow_sensor_component_gt import ( - PipeFlowSensorComponentGt, -) from gwproto.types.power_watts import PowerWatts -from gwproto.types.relay_cac_gt import RelayCacGt -from gwproto.types.relay_component_gt import RelayComponentGt from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, ) from gwproto.types.resistive_heater_component_gt import ( ResistiveHeaterComponentGt, ) -from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt from gwproto.types.rest_poller_component_gt import ( RESTPollerComponentGt, ) -from gwproto.types.simple_temp_sensor_cac_gt import ( - SimpleTempSensorCacGt, -) -from gwproto.types.simple_temp_sensor_component_gt import ( - SimpleTempSensorComponentGt, -) from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt from gwproto.types.telemetry_reporting_config import ( @@ -90,7 +67,6 @@ "EgaugeRegisterConfig", "ElectricMeterCacGt", "ElectricMeterComponentGt", - "FibaroSmartImplantCacGt", "FibaroSmartImplantComponentGt", "GtShBooleanactuatorCmdStatus", "GtShCliAtnCmd", @@ -99,25 +75,15 @@ "GtShStatus", "GtShTelemetryFromMultipurposeSensor", "HeartbeatB", - "HubitatCacGt", "HubitatComponentGt", - "HubitatPollerCacGt", "HubitatPollerComponentGt", - "HubitatTankCacGt", "HubitatTankComponentGt", "MultipurposeSensorCacGt", "MultipurposeSensorComponentGt", - "PipeFlowSensorCacGt", - "PipeFlowSensorComponentGt", "PowerWatts", - "RESTPollerCacGt", "RESTPollerComponentGt", - "RelayCacGt", - "RelayComponentGt", "ResistiveHeaterCacGt", "ResistiveHeaterComponentGt", - "SimpleTempSensorCacGt", - "SimpleTempSensorComponentGt", "SnapshotSpaceheat", "SpaceheatNodeGt", "TelemetryReportingConfig", diff --git a/src/gwproto/types/cacs.py b/src/gwproto/types/cacs.py index ee1f16e8..691e3373 100644 --- a/src/gwproto/types/cacs.py +++ b/src/gwproto/types/cacs.py @@ -1,27 +1,9 @@ from gwproto.types.electric_meter_cac_gt import ElectricMeterCacGt -from gwproto.types.fibaro_smart_implant_cac_gt import FibaroSmartImplantCacGt -from gwproto.types.hubitat_cac_gt import HubitatCacGt -from gwproto.types.hubitat_poller_cac_gt import HubitatPollerCacGt -from gwproto.types.hubitat_tank_cac_gt import HubitatTankCacGt from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt -from gwproto.types.pipe_flow_sensor_cac_gt import PipeFlowSensorCacGt -from gwproto.types.relay_cac_gt import RelayCacGt from gwproto.types.resistive_heater_cac_gt import ResistiveHeaterCacGt -from gwproto.types.rest_poller_cac_gt import RESTPollerCacGt -from gwproto.types.simple_temp_sensor_cac_gt import SimpleTempSensorCacGt -from gwproto.types.web_server_cac_gt import WebServerCacGt __all__ = [ "ElectricMeterCacGt", - "FibaroSmartImplantCacGt", - "HubitatCacGt", - "HubitatPollerCacGt", - "HubitatTankCacGt", "MultipurposeSensorCacGt", - "PipeFlowSensorCacGt", - "RESTPollerCacGt", - "RelayCacGt", "ResistiveHeaterCacGt", - "SimpleTempSensorCacGt", - "WebServerCacGt", ] diff --git a/src/gwproto/types/components.py b/src/gwproto/types/components.py index 3f038332..f617e101 100644 --- a/src/gwproto/types/components.py +++ b/src/gwproto/types/components.py @@ -7,11 +7,8 @@ from gwproto.types.hubitat_poller_component_gt import HubitatPollerComponentGt from gwproto.types.hubitat_tank_component_gt import HubitatTankComponentGt from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt -from gwproto.types.pipe_flow_sensor_component_gt import PipeFlowSensorComponentGt -from gwproto.types.relay_component_gt import RelayComponentGt from gwproto.types.resistive_heater_component_gt import ResistiveHeaterComponentGt from gwproto.types.rest_poller_component_gt import RESTPollerComponentGt -from gwproto.types.simple_temp_sensor_component_gt import SimpleTempSensorComponentGt from gwproto.types.web_server_component_gt import WebServerComponentGt __all__ = [ @@ -22,10 +19,7 @@ "HubitatPollerComponentGt", "HubitatTankComponentGt", "MultipurposeSensorComponentGt", - "PipeFlowSensorComponentGt", "RESTPollerComponentGt", - "RelayComponentGt", "ResistiveHeaterComponentGt", - "SimpleTempSensorComponentGt", "WebServerComponentGt", ] diff --git a/src/gwproto/types/fibaro_smart_implant_cac_gt.py b/src/gwproto/types/fibaro_smart_implant_cac_gt.py deleted file mode 100644 index c9106df3..00000000 --- a/src/gwproto/types/fibaro_smart_implant_cac_gt.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Literal - -from pydantic import ConfigDict - -from gwproto.types import ComponentAttributeClassGt - - -class FibaroSmartImplantCacGt(ComponentAttributeClassGt): - Model: str = "" - TypeName: Literal["fibaro.smart.implant.cac.gt"] = "fibaro.smart.implant.cac.gt" - model_config = ConfigDict(extra="allow") diff --git a/src/gwproto/types/hubitat_cac_gt.py b/src/gwproto/types/hubitat_cac_gt.py deleted file mode 100644 index 9056dfb7..00000000 --- a/src/gwproto/types/hubitat_cac_gt.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class HubitatCacGt(ComponentAttributeClassGt): - TypeName: Literal["hubitat.cac.gt"] = "hubitat.cac.gt" diff --git a/src/gwproto/types/hubitat_poller_cac_gt.py b/src/gwproto/types/hubitat_poller_cac_gt.py deleted file mode 100644 index 0b58dde9..00000000 --- a/src/gwproto/types/hubitat_poller_cac_gt.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class HubitatPollerCacGt(ComponentAttributeClassGt): - TypeName: Literal["hubitat.poller.cac.gt"] = "hubitat.poller.cac.gt" diff --git a/src/gwproto/types/hubitat_tank_cac_gt.py b/src/gwproto/types/hubitat_tank_cac_gt.py deleted file mode 100644 index b22c67a1..00000000 --- a/src/gwproto/types/hubitat_tank_cac_gt.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class HubitatTankCacGt(ComponentAttributeClassGt): - TypeName: Literal["hubitat.tank.cac.gt"] = "hubitat.tank.cac.gt" diff --git a/src/gwproto/types/pipe_flow_sensor_cac_gt.py b/src/gwproto/types/pipe_flow_sensor_cac_gt.py deleted file mode 100644 index e933937f..00000000 --- a/src/gwproto/types/pipe_flow_sensor_cac_gt.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Type pipe.flow.sensor.cac.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.types import ComponentAttributeClassGt - - -class PipeFlowSensorCacGt(ComponentAttributeClassGt): - CommsMethod: Optional[str] = None - TypeName: Literal["pipe.flow.sensor.cac.gt"] = "pipe.flow.sensor.cac.gt" diff --git a/src/gwproto/types/pipe_flow_sensor_component_gt.py b/src/gwproto/types/pipe_flow_sensor_component_gt.py deleted file mode 100644 index baa0851e..00000000 --- a/src/gwproto/types/pipe_flow_sensor_component_gt.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Type pipe.flow.sensor.component.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.types import ComponentGt - - -class PipeFlowSensorComponentGt(ComponentGt): - I2cAddress: int - ConversionFactor: float - ExpectedMaxGpmTimes100: Optional[int] = None - TypeName: Literal["pipe.flow.sensor.component.gt"] = "pipe.flow.sensor.component.gt" diff --git a/src/gwproto/types/relay_cac_gt.py b/src/gwproto/types/relay_cac_gt.py deleted file mode 100644 index 3c61d98a..00000000 --- a/src/gwproto/types/relay_cac_gt.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Type relay.cac.gt, version 000""" - -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class RelayCacGt(ComponentAttributeClassGt): - TypicalResponseTimeMs: int - TypeName: Literal["relay.cac.gt"] = "relay.cac.gt" diff --git a/src/gwproto/types/relay_component_gt.py b/src/gwproto/types/relay_component_gt.py deleted file mode 100644 index af20154f..00000000 --- a/src/gwproto/types/relay_component_gt.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Type relay.component.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.types import ComponentGt - - -class RelayComponentGt(ComponentGt): - Gpio: Optional[int] = None - NormallyOpen: bool - TypeName: Literal["relay.component.gt"] = "relay.component.gt" diff --git a/src/gwproto/types/rest_poller_cac_gt.py b/src/gwproto/types/rest_poller_cac_gt.py deleted file mode 100644 index ad642855..00000000 --- a/src/gwproto/types/rest_poller_cac_gt.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class RESTPollerCacGt(ComponentAttributeClassGt): - TypeName: Literal["rest.poller.cac.gt"] = "rest.poller.cac.gt" diff --git a/src/gwproto/types/simple_temp_sensor_cac_gt.py b/src/gwproto/types/simple_temp_sensor_cac_gt.py deleted file mode 100644 index 47159c72..00000000 --- a/src/gwproto/types/simple_temp_sensor_cac_gt.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Type simple.temp.sensor.cac.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.enums import TelemetryName, Unit -from gwproto.types import ComponentAttributeClassGt - - -class SimpleTempSensorCacGt(ComponentAttributeClassGt): - TypicalResponseTimeMs: int - Exponent: int - TempUnit: Unit - TelemetryName: TelemetryName - CommsMethod: Optional[str] = None - TypeName: Literal["simple.temp.sensor.cac.gt"] = "simple.temp.sensor.cac.gt" diff --git a/src/gwproto/types/simple_temp_sensor_component_gt.py b/src/gwproto/types/simple_temp_sensor_component_gt.py deleted file mode 100644 index 3eed5cf2..00000000 --- a/src/gwproto/types/simple_temp_sensor_component_gt.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Type simple.temp.sensor.component.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.types import ComponentGt - - -class SimpleTempSensorComponentGt(ComponentGt): - Channel: Optional[int] = None - TypeName: Literal["simple.temp.sensor.component.gt"] = ( - "simple.temp.sensor.component.gt" - ) diff --git a/src/gwproto/types/web_server_cac_gt.py b/src/gwproto/types/web_server_cac_gt.py deleted file mode 100644 index caca9c2e..00000000 --- a/src/gwproto/types/web_server_cac_gt.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Literal - -from gwproto.types import ComponentAttributeClassGt - - -class WebServerCacGt(ComponentAttributeClassGt): - TypeName: Literal["web.server.cac.gt"] = "web.server.cac.gt" From 4a1c8847a5aa6590fd07986f5b2a98074f5818b6 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 21:49:05 -0400 Subject: [PATCH 127/168] Update ShNode and TelemetryReportingConfig for SpaceheatName format --- .../json/electric-meter-component-gt.json | 90 ------------------- src/gwproto/data_classes/data_channel.py | 2 +- src/gwproto/data_classes/hardware_layout.py | 61 +++++++++++-- src/gwproto/types/spaceheat_node_gt.py | 8 +- .../types/telemetry_reporting_config.py | 6 +- tests/config/hardware-layout.json | 76 ++++++++++++---- tests/types/test_egauge_io.py | 6 +- .../types/test_electric_meter_component_gt.py | 8 +- .../test_multipurpose_sensor_component_gt.py | 4 +- tests/types/test_spaceheat_node_gt.py | 4 +- .../types/test_telemetry_reporting_config.py | 4 +- 11 files changed, 136 insertions(+), 133 deletions(-) diff --git a/docs/asls/json/electric-meter-component-gt.json b/docs/asls/json/electric-meter-component-gt.json index 625be789..8d96a728 100644 --- a/docs/asls/json/electric-meter-component-gt.json +++ b/docs/asls/json/electric-meter-component-gt.json @@ -78,96 +78,6 @@ "description": "If the EgaugeIoList has non-zero length, then the ModbusHost is not None and the set of output configs is equal to ConfigList as a set" } }, - "example": { - "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", - "ComponentId": "36a31af8-5ff6-4105-a751-fb858889bc60", - "DisplayName": "Oak EGauge6074", - "HwUid": "BP01954", - "ModbusHost": "eGauge6074.local", - "ModbusPort": 502, - "ConfigList": [ - { - "AboutNodeName": "a.m.house.panel.power", - "AsyncReportThreshold": 0.02, - "Exponent": 0, - "NameplateMaxValue": 3500, - "ReportOnChange": true, - "SamplePeriodS": 300, - "TelemetryNameGtEnumSymbol": "af39eec9", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "f459a9c3", - "Version": "000" - }, - { - "AboutNodeName": "oilpluspumpspower", - "AsyncReportThreshold": 0.02, - "Exponent": 0, - "NameplateMaxValue": 1000, - "ReportOnChange": true, - "SamplePeriodS": 300, - "TelemetryNameGtEnumSymbol": "af39eec9", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "f459a9c3", - "Version": "000" - } - ], - "EgaugeIoList": [ - { - "InputConfig": { - "Address": 9016, - "Denominator": 1, - "Description": "change in value", - "Name": "house-panel-power", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, - "OutputConfig": { - "AboutNodeName": "a.m.house.panel.power", - "AsyncReportThreshold": 0.02, - "Exponent": 0, - "NameplateMaxValue": 3500, - "ReportOnChange": true, - "SamplePeriodS": 300, - "TelemetryNameGtEnumSymbol": "af39eec9", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "f459a9c3", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "000" - }, - { - "InputConfig": { - "Address": 9018, - "Denominator": 1, - "Description": "change in value", - "Name": "oil-boiler-plus-pumps", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, - "OutputConfig": { - "AboutNodeName": "oilpluspumpspower", - "AsyncReportThreshold": 0.02, - "Exponent": 0, - "NameplateMaxValue": 1000, - "ReportOnChange": true, - "SamplePeriodS": 300, - "TelemetryNameGtEnumSymbol": "af39eec9", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "f459a9c3", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "000" - } - ], - "TypeName": "electric.meter.component.gt", - "Version": "000" - }, "formats": { "UuidCanonicalTextual": { "type": "string", diff --git a/src/gwproto/data_classes/data_channel.py b/src/gwproto/data_classes/data_channel.py index 9e193741..01cc6433 100644 --- a/src/gwproto/data_classes/data_channel.py +++ b/src/gwproto/data_classes/data_channel.py @@ -8,7 +8,7 @@ class DataChannel(DataChannelGt): about_node: ShNode captured_by_node: ShNode - model_config = ConfigDict(arbitrary_types_allowed=True) + model_config = ConfigDict(arbitrary_types_allowed=True, use_enum_values=True) def __hash__(self) -> int: return hash(self.Id) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index f92af513..bec9e7d9 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -20,6 +20,7 @@ from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) +from gwproto.data_classes.data_channel import DataChannel from gwproto.data_classes.errors import DataClassLoadingError from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode @@ -54,6 +55,7 @@ class HardwareLayout: components_by_type: dict[Type, list[Component]] nodes: dict[str, ShNode] nodes_by_component: dict[str, str] + data_channels: dict[str, DataChannel] GT_SUFFIX = "Gt" @@ -135,12 +137,9 @@ def load_components( component_decoder = default_component_decoder components = {} for type_name in [ - "RelayComponents", "ResistiveHeaterComponents", "ElectricMeterComponents", - "PipeFlowSensorComponents", "MultipurposeSensorComponents", - "SimpleTempSensorComponents", "OtherComponents", ]: for component_dict in layout.get(type_name, ()): @@ -194,6 +193,44 @@ def load_nodes( errors.append(LoadError("ShNode", node_dict, e)) return nodes + @classmethod + def make_channel(cls, dc_dict: dict, nodes: dict[str, ShNode]) -> DataChannel: + about_node = nodes.get(dc_dict.get("AboutNodeName")) + captured_by_node = nodes.get(dc_dict.get("CapturedByNodeName")) + if about_node is None or captured_by_node is None: + raise ValueError( + f"ERROR. DataChannel related nodes must exist!\n" + f" For AboutNodeName <{dc_dict.get('AboutNodeName')}> " + f"got {about_node}\n" + f" for CapturedByNodeName <{dc_dict.get('CapturedByNodeName')}>" + f"got {captured_by_node}" + ) + return DataChannel( + about_node=about_node, captured_by_node=captured_by_node, **dc_dict + ) + + @classmethod + def load_data_channels( + cls, + layout: dict[Any, Any], + nodes: dict[str, ShNode], + *, + raise_errors: bool = True, + errors: Optional[list[LoadError]] = None, + ) -> dict[str, DataChannel]: + dcs = {} + if errors is None: + errors = [] + for dc_dict in layout.get("DataChannels", []): + try: + dc_name = dc_dict["Name"] + dcs[dc_name] = cls.make_channel(dc_dict, nodes) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("DataChannel", dc_dict, e)) + return dcs + @classmethod def resolve_links( cls, @@ -232,6 +269,7 @@ def __init__( cacs: dict[str, ComponentAttributeClassGt], components: dict[str, Component], nodes: dict[str, ShNode], + data_channels: dict[str, DataChannel], ) -> None: self.layout = copy.deepcopy(layout) self.cacs = dict(cacs) @@ -243,6 +281,7 @@ def __init__( self.nodes_by_component = { node.component_id: node.alias for node in self.nodes.values() } + self.data_channels = dict(data_channels) def clear_property_cache(self) -> None: for cached_prop_name in [ @@ -300,15 +339,22 @@ def load_dict( # noqa: PLR0913 errors=errors, component_decoder=component_decoder, ) + nodes = cls.load_nodes( + layout=layout, + components=components, + raise_errors=raise_errors, + errors=errors, + included_node_names=included_node_names, + ) load_args = { "cacs": cacs, "components": components, - "nodes": cls.load_nodes( + "nodes": nodes, + "data_channels": cls.load_data_channels( layout=layout, - components=components, + nodes=nodes, raise_errors=raise_errors, errors=errors, - included_node_names=included_node_names, ), } cls.resolve_links( @@ -319,6 +365,9 @@ def load_dict( # noqa: PLR0913 ) return HardwareLayout(layout, **load_args) + def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 + return self.data_channels.get(name, default) + def node(self, alias: str, default: Any = None) -> ShNode: # noqa: ANN401 return self.nodes.get(alias, default) diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 8e4fab8d..9262d101 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -1,16 +1,16 @@ -"""Type spaceheat.node.gt, version 100""" +"""Type spaceheat.node.gt, version 110""" from typing import Literal, Optional from pydantic import BaseModel, Field from gwproto.enums import ActorClass, Role -from gwproto.property_format import LeftRightDotStr, UUID4Str +from gwproto.property_format import SpaceheatName, UUID4Str class SpaceheatNodeGt(BaseModel): ShNodeId: UUID4Str - Alias: LeftRightDotStr + Alias: SpaceheatName ActorClass: ActorClass Role: Role DisplayName: Optional[str] = None @@ -21,4 +21,4 @@ class SpaceheatNodeGt(BaseModel): default=None, ) TypeName: Literal["spaceheat.node.gt"] = "spaceheat.node.gt" - Version: Literal["100"] = "100" + Version: Literal["110"] = "110" diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py index a5785deb..2cb8dbfb 100644 --- a/src/gwproto/types/telemetry_reporting_config.py +++ b/src/gwproto/types/telemetry_reporting_config.py @@ -5,12 +5,12 @@ from pydantic import BaseModel, PositiveInt, model_validator from gwproto.enums import TelemetryName, Unit -from gwproto.property_format import LeftRightDotStr +from gwproto.property_format import SpaceheatName class TelemetryReportingConfig(BaseModel): TelemetryName: TelemetryName - AboutNodeName: LeftRightDotStr + AboutNodeName: SpaceheatName ReportOnChange: bool SamplePeriodS: int Exponent: int @@ -18,7 +18,7 @@ class TelemetryReportingConfig(BaseModel): AsyncReportThreshold: Optional[float] = None NameplateMaxValue: Optional[PositiveInt] = None TypeName: Literal["telemetry.reporting.config"] = "telemetry.reporting.config" - Version: Literal["000"] = "000" + Version: Literal["001"] = "001" def __hash__(self) -> int: return hash((type(self), *self.__dict__.values())) diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index fadb9776..9e967080 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -34,14 +34,14 @@ "DisplayName": "Power Meter for Simulated Test system", "ConfigList": [ { - "AboutNodeName": "a.elt1", + "AboutNodeName": "elt1", "ReportOnChange": true, "SamplePeriodS": 300, "Exponent": 0, "AsyncReportThreshold": 0.02, "NameplateMaxValue": 4500, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", "TelemetryName": "Power", "Unit": "W" } @@ -77,7 +77,7 @@ "ComponentId": "fe18bffa-4b46-4b03-89d4-26b3d6f3e9d1", "ConfigList": [ { - "AboutNodeName": "hp.lwt", + "AboutNodeName": "hp-lwt", "AsyncReportThreshold": 0.0025, "Exponent": 3, "NameplateMaxValue": 100000, @@ -86,10 +86,10 @@ "TelemetryName": "WaterTempCTimes1000", "TypeName": "telemetry.reporting.config", "Unit": "Celsius", - "Version": "000" + "Version": "001" }, { - "AboutNodeName": "hp.ewt", + "AboutNodeName": "hp-ewt", "AsyncReportThreshold": 0.0025, "Exponent": 3, "NameplateMaxValue": 100000, @@ -98,7 +98,7 @@ "TelemetryName": "WaterTempCTimes1000", "TypeName": "telemetry.reporting.config", "Unit": "Celsius", - "Version": "000" + "Version": "001" } ], "DisplayName": "Multipurpose Temp Sensor Component <100>", @@ -206,7 +206,7 @@ "DisplayName": "AtomicTNode", "ShNodeId": "b354edeb-0c82-4e55-80cb-7ab669ac2ad9", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { "Alias": "home", @@ -215,7 +215,7 @@ "DisplayName": "Little Orange House HomeAlone", "ShNodeId": "34470c9d-fa25-4077-909b-2f981a691d7e", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { "Alias": "s", @@ -224,7 +224,7 @@ "DisplayName": "Little Orange House Main Scada", "ShNodeId": "259f9431-c6a1-4170-8766-04cbf65cff4a", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { "Alias": "elt1", @@ -237,35 +237,79 @@ "TypicalVoltageV": 225, "InPowerMetering": true, "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { - "Alias": "power.meter", + "Alias": "power-meter", "Role": "PowerMeter", "ActorClass": "PowerMeter", "DisplayName": "Main Power Meter Little Orange House Test System", "ShNodeId": "0dd8a803-4724-4f49-b845-14ff57bdb3e6", "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { - "Alias": "hp.ewt", + "Alias": "hp-ewt", "Role": "Unknown", "ActorClass": "NoActor", "DisplayName": "Heat Pump Entering Water Temp", "ShNodeId": "e76073f4-67ae-4324-b07f-0c9add733de7", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" }, { - "Alias": "hp.lwt", + "Alias": "hp-lwt", "Role": "Unknown", "ActorClass": "NoActor", "DisplayName": "Heat Pump Leaving Water Temp", "ShNodeId": "1fe2a01f-d60a-4362-bc5e-949ddfcd5003", "TypeName": "spaceheat.node.gt", - "Version": "100" + "Version": "110" + }, + { + "Alias": "store-pump", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Store Pump", + "ShNodeId": "c35d9c77-1ebb-4822-b01e-00ff189092f7", + "TypeName": "spaceheat.node.gt", + "Version": "110" + }, + { + "Alias": "analog-temp", + "Role": "Unknown", + "ActorClass": "MultipurposeSensor", + "DisplayName": "GridWorks MultiTemp", + "ShNodeId": "8bfc40f1-7c84-4e88-9214-78c304730ae3", + "TypeName": "spaceheat.node.gt", + "Version": "110" } + ], + "DataChannels": [ + { + "Name": "store-pump-pwr", + "DisplayName": "Store pump power", + "AboutNodeName": "store-pump", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "Id": "ac35c2a9-e317-45e8-a036-52fa5cbd8380", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "hp-lwt", + "DisplayName": "Heat Pump Leaving Water Temp", + "AboutNodeName": "hp-lwt", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "Id": "ac35c2a9-e317-45e8-a036-52fa5cbd8380", + "TypeName": "data.channel.gt", + "Version": "001" + } ] } diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py index 47bc0df1..3cad137c 100644 --- a/tests/types/test_egauge_io.py +++ b/tests/types/test_egauge_io.py @@ -7,7 +7,7 @@ def test_egauge_io_generated() -> None: d = { "InputConfig": { "Address": 9004, - "Name": "Garage power", + "Name": "HP IDU Power", "Description": "", "Type": "f32", "Denominator": 1, @@ -16,14 +16,14 @@ def test_egauge_io_generated() -> None: "Version": "000", }, "OutputConfig": { - "AboutNodeName": "a.tank1.elts", + "AboutNodeName": "hp-idu", "ReportOnChange": True, "SamplePeriodS": 60, "Exponent": 0, "AsyncReportThreshold": 0.05, "NameplateMaxValue": 4500, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", "TelemetryName": "PowerW", "Unit": "W", }, diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index e62b4670..ae456778 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -12,14 +12,14 @@ def test_electric_meter_component_gt_generated() -> None: "DisplayName": "EGauge Power Meter", "ConfigList": [ { - "AboutNodeName": "a.m.hp.outdoor.power", + "AboutNodeName": "hp-odu", "ReportOnChange": True, "SamplePeriodS": 300, "Exponent": 0, "AsyncReportThreshold": 0.02, "NameplateMaxValue": 3500, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", "TelemetryName": "PowerW", "Unit": "W", } @@ -40,14 +40,14 @@ def test_electric_meter_component_gt_generated() -> None: "Version": "000", }, "OutputConfig": { - "AboutNodeName": "a.m.hp.outdoor.power", + "AboutNodeName": "hp-odu", "ReportOnChange": True, "SamplePeriodS": 300, "Exponent": 0, "AsyncReportThreshold": 0.02, "NameplateMaxValue": 3500, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", "TelemetryName": "PowerW", "Unit": "W", }, diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py index 66898a1f..581c3530 100644 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ b/tests/types/test_multipurpose_sensor_component_gt.py @@ -14,10 +14,10 @@ def test_multipurpose_sensor_component_gt_generated() -> None: { "ReportOnChange": False, "Exponent": 3, - "AboutNodeName": "a.distsourcewater.temp", + "AboutNodeName": "hp-ewt", "SamplePeriodS": 60, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", "Unit": "Celcius", "TelemetryName": "WaterTempCTimes1000", } diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 874be65e..60c5711a 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -6,7 +6,7 @@ def test_spaceheat_node_gt_generated() -> None: d = { "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", - "Alias": "a.elt1", + "Alias": "elt1", "ActorClass": "NoActor", "Role": "BoostElement", "DisplayName": "First boost element", @@ -14,6 +14,6 @@ def test_spaceheat_node_gt_generated() -> None: "ReportingSamplePeriodS": 300, "InPowerMetering": False, "TypeName": "spaceheat.node.gt", - "Version": "100", + "Version": "110", } assert SpaceheatNodeGt.model_validate(d).model_dump() == d diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py index e5620462..34fb5ac4 100644 --- a/tests/types/test_telemetry_reporting_config.py +++ b/tests/types/test_telemetry_reporting_config.py @@ -6,7 +6,7 @@ def test_telemetry_reporting_config_generated() -> None: d = { "TelemetryName": "PowerW", - "AboutNodeName": "a.elt1", + "AboutNodeName": "hp-idu", "ReportOnChange": True, "SamplePeriodS": 300, "Exponent": 6, @@ -14,6 +14,6 @@ def test_telemetry_reporting_config_generated() -> None: "AsyncReportThreshold": 0.2, "NameplateMaxValue": 4000, "TypeName": "telemetry.reporting.config", - "Version": "000", + "Version": "001", } assert TelemetryReportingConfig.model_validate(d).model_dump() == d From 5c3acfc2ad2e1a6b91d529b201b15809137806b4 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 22 Sep 2024 22:57:14 -0400 Subject: [PATCH 128/168] default sensor node name --- src/gwproto/types/hubitat_tank_gt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/types/hubitat_tank_gt.py b/src/gwproto/types/hubitat_tank_gt.py index cf6bfebd..f1cd2090 100644 --- a/src/gwproto/types/hubitat_tank_gt.py +++ b/src/gwproto/types/hubitat_tank_gt.py @@ -53,7 +53,7 @@ class FibaroTempSensorSettingsGt(BaseModel): ) -DEFAULT_SENSOR_NODE_NAME_FORMAT = "{tank_name}.temp.depth{stack_depth}" +DEFAULT_SENSOR_NODE_NAME_FORMAT = "{tank_name}-depth{stack_depth}" class FibaroTempSensorSettings(FibaroTempSensorSettingsGt): From b9107997462378ff715d44555730c8b8b30a093e Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 00:17:19 -0400 Subject: [PATCH 129/168] updates for scada --- .../gt_sh_multipurpose_telemetry_status.py | 8 +- tests/data/status_message.json | 124 ++---------------- ...est_gt_sh_multipurpose_telemetry_status.py | 6 +- tests/types/test_gt_sh_status.py | 19 +-- 4 files changed, 22 insertions(+), 135 deletions(-) diff --git a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py index 154591bc..615d5af3 100644 --- a/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py +++ b/src/gwproto/types/gt_sh_multipurpose_telemetry_status.py @@ -6,7 +6,7 @@ from gwproto.enums.telemetry_name import TelemetryName from gwproto.property_format import ( - LeftRightDotStr, + SpaceheatName, UTCMilliseconds, ) @@ -23,7 +23,7 @@ class GtShMultipurposeTelemetryStatus(BaseModel): [More info](https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html) """ - AboutNodeAlias: LeftRightDotStr = Field( + AboutNodeAlias: SpaceheatName = Field( description=( "The SpaceheatNode representing the physical object that the sensor reading is collecting " "data about. For example, a multipurpose temp sensor that reads 12 temperatures would " @@ -32,14 +32,14 @@ class GtShMultipurposeTelemetryStatus(BaseModel): "[More info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html)" ), ) - SensorNodeAlias: LeftRightDotStr + SensorNodeAlias: SpaceheatName TelemetryName: TelemetryName ValueList: list[int] ReadTimeUnixMsList: list[UTCMilliseconds] TypeName: Literal["gt.sh.multipurpose.telemetry.status"] = ( "gt.sh.multipurpose.telemetry.status" ) - Version: Literal["100"] = "100" + Version: Literal["101"] = "101" @model_validator(mode="after") def check_axiom_1(self) -> Self: diff --git a/tests/data/status_message.json b/tests/data/status_message.json index c83d4c50..025bf7af 100644 --- a/tests/data/status_message.json +++ b/tests/data/status_message.json @@ -14,145 +14,41 @@ "FromGNodeId": "28817671-3899-4e24-a337-abcb8633e47a", "MultipurposeTelemetryList": [ { - "AboutNodeAlias": "a.elt1", + "AboutNodeAlias": "elt1", "ReadTimeUnixMsList": [1676592055586], - "SensorNodeAlias": "a.m", + "SensorNodeAlias": "power-meter", "TelemetryName": "PowerW", "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", + "Version": "101", "ValueList": [0] }, { - "AboutNodeAlias": "a.tank.out.temp2", + "AboutNodeAlias": "store-hot-pipe", "ReadTimeUnixMsList": [ 1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089 ], - "SensorNodeAlias": "a.s.analog.temp", + "SensorNodeAlias": "analog-temp", "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", + "Version": "101", "ValueList": [77, 361, -115, -9, -44] }, { - "AboutNodeAlias": "a.tank.in.temp2", + "AboutNodeAlias": "store-cold-pipe", "ReadTimeUnixMsList": [ 1676592000232, 1676592060801, 1676592120037, 1676592180555, 1676592240089 ], - "SensorNodeAlias": "a.s.analog.temp", + "SensorNodeAlias": "analog-temp", "TelemetryName": "WaterTempCTimes1000", "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", + "Version": "101", "ValueList": [-3587, -3574, -3771, -3656, -3626] - }, - { - "AboutNodeAlias": "a.garage.temp2", - "ReadTimeUnixMsList": [ - 1676592000232, 1676592060801, 1676592120037, 1676592180555, - 1676592240089 - ], - "SensorNodeAlias": "a.s.analog.temp", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", - "ValueList": [-3222, -3036, -3264, -3087, -3015] - }, - { - "AboutNodeAlias": "a.tankoutside.temp2", - "ReadTimeUnixMsList": [ - 1676592000232, 1676592060801, 1676592120037, 1676592180555, - 1676592240089 - ], - "SensorNodeAlias": "a.s.analog.temp", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", - "ValueList": [3011, 3037, 2893, 3037, 3041] } ], "ReportingPeriodS": 300, - "SimpleTelemetryList": [ - { - "ReadTimeUnixMsList": [1676592052038], - "ShNodeAlias": "a.elt1.relay", - "TelemetryName": "RelayState", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [0] - }, - { - "ReadTimeUnixMsList": [1676592232171], - "ShNodeAlias": "a.tank.out.pump.relay", - "TelemetryName": "RelayState", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [0] - }, - { - "ReadTimeUnixMsList": [1676592079160], - "ShNodeAlias": "a.tank.out.pump.baseboard1.fan.relay", - "TelemetryName": "RelayState", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [0] - }, - { - "ReadTimeUnixMsList": [ - 1676592033601, 1676592094057, 1676592154524, 1676592215130, - 1676592275685 - ], - "ShNodeAlias": "a.tank.out.temp1", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [18000, 17937, 17937, 17875, 17812] - }, - { - "ReadTimeUnixMsList": [ - 1676592044721, 1676592104326, 1676592164389, 1676592224395, - 1676592284085 - ], - "ShNodeAlias": "a.tank.out.far.temp1", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [15375, 15312, 15312, 15312, 15250] - }, - { - "ReadTimeUnixMsList": [ - 1676592034321, 1676592095091, 1676592155316, 1676592215161, - 1676592275192 - ], - "ShNodeAlias": "a.tank.in.temp1", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [14437, 14375, 14312, 14312, 14312] - }, - { - "ReadTimeUnixMsList": [ - 1676592035601, 1676592095132, 1676592155706, 1676592215733, - 1676592275380 - ], - "ShNodeAlias": "a.tank.temp0", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [21812, 21812, 21812, 21812, 21812] - }, - { - "ReadTimeUnixMsList": [ - 1676592024051, 1676592084160, 1676592144739, 1676592204239, - 1676592264435 - ], - "ShNodeAlias": "a.garage.temp1", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "ValueList": [15125, 15062, 15062, 15062, 15062] - } - ], + "SimpleTelemetryList": [], "SlotStartUnixS": 1676592000, "StatusUid": "82abba64-d0df-4907-9b2b-6cb585421068", "TypeName": "gt.sh.status", diff --git a/tests/types/test_gt_sh_multipurpose_telemetry_status.py b/tests/types/test_gt_sh_multipurpose_telemetry_status.py index 00a6b441..cd0dab47 100644 --- a/tests/types/test_gt_sh_multipurpose_telemetry_status.py +++ b/tests/types/test_gt_sh_multipurpose_telemetry_status.py @@ -5,12 +5,12 @@ def test_gt_sh_multipurpose_telemetry_status_generated() -> None: d = { - "AboutNodeAlias": "a.elt1", - "SensorNodeAlias": "a.m", + "AboutNodeAlias": "elt1", + "SensorNodeAlias": "power-meter", "TelemetryName": "PowerW", "ValueList": [4559], "ReadTimeUnixMsList": [1656443705023], "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", + "Version": "101", } assert GtShMultipurposeTelemetryStatus.model_validate(d).model_dump() == d diff --git a/tests/types/test_gt_sh_status.py b/tests/types/test_gt_sh_status.py index 8f8b90da..303789cb 100644 --- a/tests/types/test_gt_sh_status.py +++ b/tests/types/test_gt_sh_status.py @@ -10,30 +10,21 @@ def test_gt_sh_status_generated() -> None: "AboutGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta", "SlotStartUnixS": 1656945300, "ReportingPeriodS": 300, - "SimpleTelemetryList": [ - { - "ValueList": [0, 1], - "ReadTimeUnixMsList": [1656945400527, 1656945414270], - "ShNodeAlias": "a.elt1.relay", - "TypeName": "gt.sh.simple.telemetry.status", - "Version": "100", - "TelemetryName": "RelayState", - } - ], + "SimpleTelemetryList": [], "MultipurposeTelemetryList": [ { - "AboutNodeAlias": "a.elt1", + "AboutNodeAlias": "elt1", "ValueList": [18000], "ReadTimeUnixMsList": [1656945390152], - "SensorNodeAlias": "a.m", + "SensorNodeAlias": "power-meter", "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "100", + "Version": "101", "TelemetryName": "CurrentRmsMicroAmps", } ], "BooleanactuatorCmdList": [ { - "ShNodeAlias": "a.elt1.relay", + "ShNodeAlias": "elt1", "RelayStateCommandList": [1], "CommandTimeUnixMsList": [1656945413464], "TypeName": "gt.sh.booleanactuator.cmd.status", From e3adc507f359e37e6ad87031543666538826cb58 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:08:15 -0400 Subject: [PATCH 130/168] Add ConfigList to ComponentGt -also replace MultipurposeSensorCac/Comp with Ads111xBasedCac/Comp - Added ChannelConfig (the type of ConfigList) and removed TelemetryReportingConfig - Added subtypes and enums needed for Ads111xBasedCac - removed data_classes/errors -> will use gw.errors.DcError --- .../components/ads111x_based_component.py | 7 + .../multipurpose_sensor_component.py | 10 - src/gwproto/data_classes/errors.py | 6 - src/gwproto/enums/thermistor_data_method.py | 62 +++++ .../types/ads111x_based_component_gt.py | 58 ++++ src/gwproto/types/channel_config.py | 43 +++ src/gwproto/types/component_gt.py | 6 +- src/gwproto/types/components.py | 4 +- src/gwproto/types/egauge_io.py | 37 +-- .../types/electric_meter_component_gt.py | 62 ++--- .../types/multipurpose_sensor_cac_gt.py | 17 -- .../types/multipurpose_sensor_component_gt.py | 16 -- .../types/telemetry_reporting_config.py | 36 --- .../thermistor_data_processing_config.py | 24 ++ tests/enums/thermistor_data_method_test.py | 19 ++ tests/types/test_ads111x_based_cac_gt.py | 21 ++ .../types/test_ads111x_based_component_gt.py | 263 ++++++++++++++++++ tests/types/test_channel_config.py | 30 ++ tests/types/test_electric_meter_cac_gt.py | 23 +- .../types/test_electric_meter_component_gt.py | 174 ++++++++++-- .../types/test_multipurpose_sensor_cac_gt.py | 21 -- .../test_multipurpose_sensor_component_gt.py | 39 --- .../test_resistive_heater_component_gt.py | 7 +- .../types/test_telemetry_reporting_config.py | 19 -- .../test_thermistor_data_processing_config.py | 39 +++ 25 files changed, 779 insertions(+), 264 deletions(-) create mode 100644 src/gwproto/data_classes/components/ads111x_based_component.py delete mode 100644 src/gwproto/data_classes/components/multipurpose_sensor_component.py delete mode 100644 src/gwproto/data_classes/errors.py create mode 100644 src/gwproto/enums/thermistor_data_method.py create mode 100644 src/gwproto/types/ads111x_based_component_gt.py create mode 100644 src/gwproto/types/channel_config.py delete mode 100644 src/gwproto/types/multipurpose_sensor_cac_gt.py delete mode 100644 src/gwproto/types/multipurpose_sensor_component_gt.py delete mode 100644 src/gwproto/types/telemetry_reporting_config.py create mode 100644 src/gwproto/types/thermistor_data_processing_config.py create mode 100644 tests/enums/thermistor_data_method_test.py create mode 100644 tests/types/test_ads111x_based_cac_gt.py create mode 100644 tests/types/test_ads111x_based_component_gt.py create mode 100644 tests/types/test_channel_config.py delete mode 100644 tests/types/test_multipurpose_sensor_cac_gt.py delete mode 100644 tests/types/test_multipurpose_sensor_component_gt.py delete mode 100644 tests/types/test_telemetry_reporting_config.py create mode 100644 tests/types/test_thermistor_data_processing_config.py diff --git a/src/gwproto/data_classes/components/ads111x_based_component.py b/src/gwproto/data_classes/components/ads111x_based_component.py new file mode 100644 index 00000000..4007e8a8 --- /dev/null +++ b/src/gwproto/data_classes/components/ads111x_based_component.py @@ -0,0 +1,7 @@ +"""Ads111xBasedComponent definition""" + +from gwproto.data_classes.components.component import Component +from gwproto.types import Ads111xBasedCacGt, Ads111xBasedComponentGt + + +class Ads111xBasedComponent(Component[Ads111xBasedComponentGt, Ads111xBasedCacGt]): ... diff --git a/src/gwproto/data_classes/components/multipurpose_sensor_component.py b/src/gwproto/data_classes/components/multipurpose_sensor_component.py deleted file mode 100644 index 42f382a2..00000000 --- a/src/gwproto/data_classes/components/multipurpose_sensor_component.py +++ /dev/null @@ -1,10 +0,0 @@ -"""MutlipurposeSensorComponent definition""" - -from gwproto.data_classes.components.component import Component -from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt -from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt - - -class MultipurposeSensorComponent( - Component[MultipurposeSensorComponentGt, MultipurposeSensorCacGt] -): ... diff --git a/src/gwproto/data_classes/errors.py b/src/gwproto/data_classes/errors.py deleted file mode 100644 index fee027ff..00000000 --- a/src/gwproto/data_classes/errors.py +++ /dev/null @@ -1,6 +0,0 @@ -class DcError(Exception): - """Base class for dataclass errors""" - - -class DataClassLoadingError(Exception): - """data class loading error""" diff --git a/src/gwproto/enums/thermistor_data_method.py b/src/gwproto/enums/thermistor_data_method.py new file mode 100644 index 00000000..8b16d766 --- /dev/null +++ b/src/gwproto/enums/thermistor_data_method.py @@ -0,0 +1,62 @@ +from enum import auto +from typing import Optional + +from gw.enums import GwStrEnum + + +class ThermistorDataMethod(GwStrEnum): + """ + What method is used to go from raw voltage readings to captured temperature readings. + + Enum thermistor.data.method version 000 in the GridWorks Type registry. + + Used by multiple Application Shared Languages (ASLs). For more information: + - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) + - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#thermistordatamethod) + + Values: + - SimpleBeta: Using the beta formula with a calibrated open voltage reading, transmitting + raw polled data. + - BetaWithExponentialAveraging: Using the beta formula with a calibrated open voltage + reading, and then some sort of exponential weighted averaging on polled data. + """ + + SimpleBeta = auto() + BetaWithExponentialAveraging = auto() + + @classmethod + def default(cls) -> "ThermistorDataMethod": + """ + Returns default value (in this case SimpleBeta) + """ + return cls.SimpleBeta + + @classmethod + def version(cls, value: Optional[str] = None) -> str: + if value is None: + return "000" + if not isinstance(value, str): + raise TypeError("This method applies to strings, not enums") + if value not in value_to_version: + raise ValueError(f"Unknown enum value: {value}") + return value_to_version[value] + + @classmethod + def enum_name(cls) -> str: + """ + The name in the GridWorks Type Registry (thermistor.data.method) + """ + return "thermistor.data.method" + + @classmethod + def enum_version(cls) -> str: + """ + The version in the GridWorks Type Registry (000) + """ + return "000" + + +value_to_version = { + "SimpleBeta": "000", + "BetaWithExponentialAveraging": "000", +} diff --git a/src/gwproto/types/ads111x_based_component_gt.py b/src/gwproto/types/ads111x_based_component_gt.py new file mode 100644 index 00000000..f40d76f5 --- /dev/null +++ b/src/gwproto/types/ads111x_based_component_gt.py @@ -0,0 +1,58 @@ +"""Type ads111x.based.component.gt, version 000""" + +from typing import List, Literal + +from pydantic import ConfigDict, field_validator, model_validator +from typing_extensions import Self + +from gwproto.property_format import ( + check_is_near5, +) +from gwproto.types.component_gt import ComponentGt +from gwproto.types.thermistor_data_processing_config import ( + ThermistorDataProcessingConfig, +) + + +class Ads111xBasedComponentGt(ComponentGt): + OpenVoltageByAds: List[float] + ThermistorConfigList: List[ThermistorDataProcessingConfig] + TypeName: Literal["ads111x.based.component.gt"] = "ads111x.based.component.gt" + Version: Literal["000"] = "000" + + model_config = ConfigDict(use_enum_values=True) + + @field_validator("OpenVoltageByAds") + @classmethod + def _check_open_voltage_by_ads(cls, v: List[float]) -> List[float]: + try: + for elt in v: + check_is_near5(elt) + except ValueError as e: + raise ValueError( + f"OpenVoltageByAds element failed Near5 format validation: {e}", + ) from e + return v + + @field_validator("ThermistorConfigList") + @classmethod + def check_thermistor_config_list( + cls, v: List[ThermistorDataProcessingConfig] + ) -> List[ThermistorDataProcessingConfig]: + """ + Axiom 1: Terminal Block consistency and Channel Name uniqueness.. + Terminal Block consistency and Channel Name uniqueness. - Each TerminalBlockIdx occurs at + most once in the ThermistorConfigList - Each data channel occurs at most once in the ThermistorConfigList + """ + # Implement Axiom(s) + return v + + @model_validator(mode="after") + def check_axiom_2(self) -> Self: + """ + Axiom 2: ThermistorConfig, ChannelConfig consistency. + set(map(lambda x: x.ChannelName, ThermistorConfigList)) is equal to + set(map(lambda x: x.ChannelName, ConfigList)) + """ + # Implement check for axiom 2" + return self diff --git a/src/gwproto/types/channel_config.py b/src/gwproto/types/channel_config.py new file mode 100644 index 00000000..10a29da1 --- /dev/null +++ b/src/gwproto/types/channel_config.py @@ -0,0 +1,43 @@ +"""Type channel.config, version 000""" + +from typing import Literal, Optional + +from pydantic import BaseModel, ConfigDict, PositiveInt, StrictInt, model_validator +from typing_extensions import Self + +from gwproto.enums import Unit +from gwproto.property_format import ( + SpaceheatName, +) + + +class ChannelConfig(BaseModel): + ChannelName: SpaceheatName + PollPeriodMs: PositiveInt + CapturePeriodS: PositiveInt + AsyncCapture: bool + AsyncCaptureDelta: Optional[PositiveInt] = None + Exponent: StrictInt + Unit: Unit + TypeName: Literal["channel.config"] = "channel.config" + Version: Literal["000"] = "000" + + model_config = ConfigDict(use_enum_values=True) + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: Async Capture Consistency. + If AsyncCapture is True, then AsyncCaptureDelta exists + """ + # Implement check for axiom 1" + return self + + @model_validator(mode="after") + def check_axiom_2(self) -> Self: + """ + Axiom 2: Capture and Polling Consistency. + CapturePeriodMs (CapturePeriodS * 1000) must be larger than PollPeriodMs. If CapturePeriodMs < 10 * PollPeriodMs then CapturePeriodMs must be a multiple of PollPeriodMs. + """ + # Implement check for axiom 2" + return self diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index 7eb60519..a84c06eb 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -1,16 +1,18 @@ """Type component.gt, version 000""" -from typing import Literal, Optional +from typing import List, Literal, Optional from pydantic import BaseModel from gwproto.property_format import UUID4Str +from gwproto.types.channel_config import ChannelConfig class ComponentGt(BaseModel): ComponentId: UUID4Str ComponentAttributeClassId: UUID4Str + ConfigList: List[ChannelConfig] DisplayName: Optional[str] = None HwUid: Optional[str] = None TypeName: Literal["component.gt"] = "component.gt" - Version: Literal["000"] = "000" + Version: Literal["001"] = "001" diff --git a/src/gwproto/types/components.py b/src/gwproto/types/components.py index f617e101..86cc331b 100644 --- a/src/gwproto/types/components.py +++ b/src/gwproto/types/components.py @@ -1,3 +1,4 @@ +from gwproto.types.ads111x_based_component_gt import Ads111xBasedComponentGt from gwproto.types.component_gt import ComponentGt from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt from gwproto.types.fibaro_smart_implant_component_gt import ( @@ -6,19 +7,18 @@ from gwproto.types.hubitat_component_gt import HubitatComponentGt from gwproto.types.hubitat_poller_component_gt import HubitatPollerComponentGt from gwproto.types.hubitat_tank_component_gt import HubitatTankComponentGt -from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt from gwproto.types.resistive_heater_component_gt import ResistiveHeaterComponentGt from gwproto.types.rest_poller_component_gt import RESTPollerComponentGt from gwproto.types.web_server_component_gt import WebServerComponentGt __all__ = [ "ComponentGt", + "Ads111xBasedComponentGt", "ElectricMeterComponentGt", "FibaroSmartImplantComponentGt", "HubitatComponentGt", "HubitatPollerComponentGt", "HubitatTankComponentGt", - "MultipurposeSensorComponentGt", "RESTPollerComponentGt", "ResistiveHeaterComponentGt", "WebServerComponentGt", diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py index 2b1d1976..5c19620e 100644 --- a/src/gwproto/types/egauge_io.py +++ b/src/gwproto/types/egauge_io.py @@ -2,41 +2,18 @@ from typing import Literal -from pydantic import BaseModel, Field +from pydantic import BaseModel +from gwproto.property_format import ( + SpaceheatName, +) from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, ) -from gwproto.types.telemetry_reporting_config import ( - TelemetryReportingConfig, -) class EgaugeIo(BaseModel): - """ - Used for an eGauge meter's component information in a hardware layout. - - When the component associated to a PowerMeter ShNode has MakeModel EGAUGE__4030, there is - a significant amount of configuration required to specify both what is read from the eGauge - (input) and what is then sent up to the SCADA (output). This type handles that information. - - [More info](https://gridworks-protocol.readthedocs.io/en/latest/egauge-io.html) - """ - - InputConfig: EgaugeRegisterConfig = Field( - title="Input config for one channel of data for a specific eGauge meter", - description=( - "This is the data available from the modbus csv map provided by eGauge for this component, " - "for example http://egauge14875.egaug.es/6001C/settings.html for a eGauge device " - "with ID 14875" - ), - ) - OutputConfig: TelemetryReportingConfig = Field( - title="Output config for the same channel ", - description=( - "This is the data as the Scada proactor expects to consume it from the power meter " - "driver proactor." - ), - ) + ChannelName: SpaceheatName + InputConfig: EgaugeRegisterConfig TypeName: Literal["egauge.io"] = "egauge.io" - Version: Literal["000"] = "000" + Version: Literal["001"] = "001" diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index 5749ac51..ccc56f29 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -1,47 +1,45 @@ """Type electric.meter.component.gt, version 000""" -from typing import Literal, Optional, Self +from typing import List, Literal, Optional, Self -from pydantic import Field, PositiveInt, model_validator +from pydantic import PositiveInt, model_validator from gwproto.types import ComponentGt from gwproto.types.egauge_io import EgaugeIo -from gwproto.types.telemetry_reporting_config import ( - TelemetryReportingConfig, -) class ElectricMeterComponentGt(ComponentGt): - ConfigList: list[TelemetryReportingConfig] ModbusHost: Optional[str] = None ModbusPort: Optional[PositiveInt] = None - EgaugeIoList: list[EgaugeIo] = Field( - default=[], - title="Bijecton from EGauge4030 input to ConfigList output", - description=( - "This should be empty unless the MakeModel of the corresponding component attribute " - "class is EGauge 4030. The channels that can be read from an EGauge 4030 are configurable " - "by the person who installs the device. The information is encapsulated in a modbus " - "map provided by eGauge as a csv from a device-specific API. The EGaugeIoList maps " - "the data from this map to the data that the SCADA expects to see." - ), - ) + EgaugeIoList: List[EgaugeIo] TypeName: Literal["electric.meter.component.gt"] = "electric.meter.component.gt" - Version: Literal["000"] = "000" @model_validator(mode="after") - def check_axioms(self) -> Self: - if self.ModbusHost is not None and self.ModbusPort is None: - raise ValueError("Axiom 1: ModbusHost None iff ModbusPort None! ") - if len(self.EgaugeIoList) == 0: - return self - if self.ModbusHost is None: - raise ValueError( - "Axiom 2: If EgaugeIoList has non-zero length then ModbusHost must exist!" - ) - if {x.OutputConfig for x in self.EgaugeIoList} != set(self.ConfigList): - raise ValueError( - "Axiom 2: If EgaugeIoList has non-zero length then the set of" - "output configs must equal ConfigList as a set" - ) + def check_axiom_1(self) -> Self: + """ + Axiom 1: Modbus consistency. + ModbusHost is None if and only if ModbusPort is None + """ + # Implement check for axiom 1" + return self + + @model_validator(mode="after") + def check_axiom_2(self) -> Self: + """ + Axiom 2: Egauge4030 Means Modbus. + If the EgaugeIoList has non-zero length, then the ModbusHost is not None + """ + # Implement check for axiom 2" + return self + + @model_validator(mode="after") + def check_axiom_3(self) -> Self: + """ + Axiom 3: Channel Name Consistency. + If the EgaugeIoList has non-zero length: + 1) Len(EgaugeIoList) == Len(ConfigList) + 2) There are no duplicates of ChannelName in the ConfigList or EgaugeIoList + 3) The set of ChannelNames in IoConfig is equal to the set of ChannelNames in ConfigList + """ + # Implement check for axiom 3" return self diff --git a/src/gwproto/types/multipurpose_sensor_cac_gt.py b/src/gwproto/types/multipurpose_sensor_cac_gt.py deleted file mode 100644 index a854a097..00000000 --- a/src/gwproto/types/multipurpose_sensor_cac_gt.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Type multipurpose.sensor.cac.gt, version 000""" - -from typing import Literal, Optional - -from gwproto.enums import TelemetryName, Unit -from gwproto.types import ComponentAttributeClassGt - - -class MultipurposeSensorCacGt(ComponentAttributeClassGt): - PollPeriodMs: int - Exponent: int - TempUnit: Unit - TelemetryNameList: list[TelemetryName] - MaxThermistors: Optional[int] = None - DisplayName: Optional[str] = None - CommsMethod: Optional[str] = None - TypeName: Literal["multipurpose.sensor.cac.gt"] = "multipurpose.sensor.cac.gt" diff --git a/src/gwproto/types/multipurpose_sensor_component_gt.py b/src/gwproto/types/multipurpose_sensor_component_gt.py deleted file mode 100644 index 6368791f..00000000 --- a/src/gwproto/types/multipurpose_sensor_component_gt.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Type multipurpose.sensor.component.gt, version 000""" - -from typing import List, Literal - -from gwproto.types import ComponentGt -from gwproto.types.telemetry_reporting_config import ( - TelemetryReportingConfig, -) - - -class MultipurposeSensorComponentGt(ComponentGt): - ChannelList: list[int] - ConfigList: List[TelemetryReportingConfig] - TypeName: Literal["multipurpose.sensor.component.gt"] = ( - "multipurpose.sensor.component.gt" - ) diff --git a/src/gwproto/types/telemetry_reporting_config.py b/src/gwproto/types/telemetry_reporting_config.py deleted file mode 100644 index 2cb8dbfb..00000000 --- a/src/gwproto/types/telemetry_reporting_config.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Type telemetry.reporting.config, version 000""" - -from typing import Literal, Optional, Self - -from pydantic import BaseModel, PositiveInt, model_validator - -from gwproto.enums import TelemetryName, Unit -from gwproto.property_format import SpaceheatName - - -class TelemetryReportingConfig(BaseModel): - TelemetryName: TelemetryName - AboutNodeName: SpaceheatName - ReportOnChange: bool - SamplePeriodS: int - Exponent: int - Unit: Unit - AsyncReportThreshold: Optional[float] = None - NameplateMaxValue: Optional[PositiveInt] = None - TypeName: Literal["telemetry.reporting.config"] = "telemetry.reporting.config" - Version: Literal["001"] = "001" - - def __hash__(self) -> int: - return hash((type(self), *self.__dict__.values())) - - @model_validator(mode="after") - def check_axiom_1(self) -> Self: - """ - Axiom 1: Async reporting consistency. - If AsyncReportThreshold exists, so does NameplateMaxValue - """ - if self.AsyncReportThreshold is not None and self.NameplateMaxValue is None: - raise ValueError( - "Violates Axiom 1: If AsyncReportThreshold exists, so does NameplateMaxValue" - ) - return self diff --git a/src/gwproto/types/thermistor_data_processing_config.py b/src/gwproto/types/thermistor_data_processing_config.py new file mode 100644 index 00000000..137752bc --- /dev/null +++ b/src/gwproto/types/thermistor_data_processing_config.py @@ -0,0 +1,24 @@ +"""Type thermistor.data.processing.config, version 000""" + +from typing import Literal, Optional + +from pydantic import BaseModel, ConfigDict, PositiveInt + +from gwproto.enums import MakeModel, ThermistorDataMethod +from gwproto.property_format import ( + SpaceheatName, +) + + +class ThermistorDataProcessingConfig(BaseModel): + ChannelName: SpaceheatName + TerminalBlockIdx: PositiveInt + ThermistorMakeModel: MakeModel + DataProcessingMethod: Optional[ThermistorDataMethod] = None + DataProcessingDescription: Optional[str] = None + TypeName: Literal["thermistor.data.processing.config"] = ( + "thermistor.data.processing.config" + ) + Version: Literal["000"] = "000" + + model_config = ConfigDict(extra="allow", use_enum_values=True) diff --git a/tests/enums/thermistor_data_method_test.py b/tests/enums/thermistor_data_method_test.py new file mode 100644 index 00000000..c7f4b07f --- /dev/null +++ b/tests/enums/thermistor_data_method_test.py @@ -0,0 +1,19 @@ +""" +Tests for enum thermistor.data.method.000 from the GridWorks Type Registry. +""" + +from gwproto.enums import ThermistorDataMethod + + +def test_thermistor_data_method() -> None: + assert set(ThermistorDataMethod.values()) == { + "SimpleBeta", + "BetaWithExponentialAveraging", + } + + assert ThermistorDataMethod.default() == ThermistorDataMethod.SimpleBeta + assert ThermistorDataMethod.enum_name() == "thermistor.data.method" + assert ThermistorDataMethod.enum_version() == "000" + + assert ThermistorDataMethod.version("SimpleBeta") == "000" + assert ThermistorDataMethod.version("BetaWithExponentialAveraging") == "000" diff --git a/tests/types/test_ads111x_based_cac_gt.py b/tests/types/test_ads111x_based_cac_gt.py new file mode 100644 index 00000000..a76b3488 --- /dev/null +++ b/tests/types/test_ads111x_based_cac_gt.py @@ -0,0 +1,21 @@ +"""Tests ads111x.based.cac.gt type, version 000""" + +from gwproto.types import Ads111xBasedCacGt + + +def test_ads111x_based_cac_gt_generated() -> None: + d = { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "MinPollPeriodMs": 200, + "MakeModel": "GRIDWORKS__MULTITEMP1", + "AdsI2cAddressList": ["0x4b", "0x48", "0x49"], + "TotalTerminalBlocks": 12, + "TelemetryNameList": ["WaterTempCTimes1000", "AirTempCTimes1000"], + "DisplayName": "Gridworks 12-channel MultiTemp Ads Sensor", + "TypeName": "ads111x.based.cac.gt", + "Version": "000", + } + + d2 = Ads111xBasedCacGt.model_validate(d).model_dump(exclude_none=True) + assert type(d2["MakeModel"]) is str + assert d2 == d diff --git a/tests/types/test_ads111x_based_component_gt.py b/tests/types/test_ads111x_based_component_gt.py new file mode 100644 index 00000000..620f03d8 --- /dev/null +++ b/tests/types/test_ads111x_based_component_gt.py @@ -0,0 +1,263 @@ +"""Tests ads111x.based.component.gt type, version 000""" + +from gwproto.types import Ads111xBasedComponentGt + + +def test_ads111x_based_component_gt_generated() -> None: + d = { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", + "ConfigList": [ + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-swt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-rwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-lwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-ewt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "ChannelName": "oat", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": False, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 5, + "ChannelName": "zone2-up-gw-temp", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": False, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 5, + "ChannelName": "zone1-down-gw-temp", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-well", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000", + }, + ], + "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", + "OpenVoltageByAds": [4.95, 4.95, 4.95], + "ThermistorConfigList": [ + { + "ChannelName": "dist-swt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 1, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "dist-rwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 2, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "hp-lwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 3, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "hp-ewt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 4, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "store-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 5, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "store-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 6, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "buffer-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 7, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "buffer-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 8, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "oat", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 9, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "zone2-up-gw-temp", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 10, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "zone1-down-gw-temp", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 11, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + { + "ChannelName": "buffer-well", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 12, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + }, + ], + "TypeName": "ads111x.based.component.gt", + "Version": "000", + } + + d2 = Ads111xBasedComponentGt.model_validate(d).model_dump(exclude_none=True) + + assert type(d2["ThermistorConfigList"][0]["DataProcessingMethod"]) is str + assert d2 == d diff --git a/tests/types/test_channel_config.py b/tests/types/test_channel_config.py new file mode 100644 index 00000000..fe06a4d0 --- /dev/null +++ b/tests/types/test_channel_config.py @@ -0,0 +1,30 @@ +"""Tests channel.config type, version 000""" + +from gwproto.enums import Unit +from gwproto.types import ChannelConfig + + +def test_channel_config_generated() -> None: + d = { + "ChannelName": "hp-idu-pwr", + "PollPeriodMs": 300, + "CapturePeriodS": 60, + "AsyncCapture": True, + "AsyncCaptureDelta": 30, + "Exponent": 0, + "Unit": "W", + "TypeName": "channel.config", + "Version": "000", + } + + d2 = ChannelConfig.model_validate(d).model_dump(exclude_none=True) + + assert type(d2["Unit"]) is str + assert d2 == d + + ###################################### + # Behavior on unknown enum values: sends to default + ###################################### + + d2 = dict(d, Unit="unknown_enum_thing") + assert ChannelConfig(**d2).Unit == Unit.default() diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index c3aec33f..a0cfe472 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -1,5 +1,9 @@ """Tests electric.meter.cac.gt type, version 000""" +import pytest +from pydantic import ValidationError +from gwproto.enums import MakeModel +from gwproto.type_helpers import CACS_BY_MAKE_MODEL from gwproto.types import ElectricMeterCacGt from tests.cac_load_utils import CacCase, assert_cac_load @@ -7,13 +11,28 @@ def test_electric_meter_cac_load() -> None: d = { "ComponentAttributeClassId": "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", + "MinPollPeriodMs": 1000, "MakeModel": "SCHNEIDERELECTRIC__IEM3455", "DisplayName": "Schneider Electric Iem3455 Power Meter", "TelemetryNameList": ["PowerW"], "PollPeriodMs": 1000, - "Interface": "RS485", "DefaultBaud": 9600, "TypeName": "electric.meter.cac.gt", - "Version": "000", + "Version": "001", } assert_cac_load([CacCase("ElectricMeterCac", d, ElectricMeterCacGt)]) + + d2 = ElectricMeterCacGt.model_validate(d).model_dump(exclude_none=True) + assert type(d2["MakeModel"]) is str + + d2 = dict( + d, + MakeModel="unknown_enum_thing", + ComponentAttributeClassId="c00ec7bd-332a-4647-b08a-b00705adee2d", + ) + assert ElectricMeterCacGt(**d2).MakeModel == MakeModel.default() + + d2 = dict(d, ComponentAttributeClassId=CACS_BY_MAKE_MODEL[MakeModel.ADAFRUIT__642]) + + with pytest.raises(ValidationError): + ElectricMeterCacGt.model_validate(d2) diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index ae456778..44bc9b60 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -6,57 +6,175 @@ def test_electric_meter_component_gt_generated() -> None: - d = { - "ComponentId": "2dfb0cb6-6015-4273-b02b-bd446cc785d7", + d = d = { "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", - "DisplayName": "EGauge Power Meter", + "ComponentId": "dce5c63d-8a38-439e-88ac-dd5ad845e9ca", "ConfigList": [ { - "AboutNodeName": "hp-odu", - "ReportOnChange": True, - "SamplePeriodS": 300, + "AsyncCapture": True, + "AsyncCaptureDelta": 200, + "CapturePeriodS": 300, + "ChannelName": "hp-odu-pwr", "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 3500, - "TypeName": "telemetry.reporting.config", - "Version": "001", - "TelemetryName": "PowerW", + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 50, + "CapturePeriodS": 300, + "ChannelName": "hp-idu-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, + "ChannelName": "primary-pump-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, + "ChannelName": "dist-pump-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, + "ChannelName": "store-pump-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", "Unit": "W", - } + "Version": "000", + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, + "ChannelName": "oil-boiler-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000", + }, ], - "HwUid": "BP01349", - "ModbusHost": "eGauge6069.local", - "ModbusPort": 502, + "DisplayName": "eGauge6069.local", "EgaugeIoList": [ { + "ChannelName": "hp-odu-pwr", "InputConfig": { "Address": 9006, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000", + }, + "TypeName": "egauge.io", + "Version": "001", + }, + { + "ChannelName": "hp-idu-pwr", + "InputConfig": { + "Address": 9000, + "Denominator": 1, + "Description": "change in value", "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000", + }, + "TypeName": "egauge.io", + "Version": "001", + }, + { + "ChannelName": "primary-pump-pwr", + "InputConfig": { + "Address": 9012, + "Denominator": 1, "Description": "change in value", + "Name": "", "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000", + }, + "TypeName": "egauge.io", + "Version": "001", + }, + { + "ChannelName": "dist-pump-pwr", + "InputConfig": { + "Address": 9010, "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", "Unit": "W", + "Version": "000", + }, + "TypeName": "egauge.io", + "Version": "001", + }, + { + "ChannelName": "store-pump-pwr", + "InputConfig": { + "Address": 9014, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", "TypeName": "egauge.register.config", + "Unit": "W", "Version": "000", }, - "OutputConfig": { - "AboutNodeName": "hp-odu", - "ReportOnChange": True, - "SamplePeriodS": 300, - "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 3500, - "TypeName": "telemetry.reporting.config", - "Version": "001", - "TelemetryName": "PowerW", + "TypeName": "egauge.io", + "Version": "001", + }, + { + "ChannelName": "oil-boiler-pwr", + "InputConfig": { + "Address": 9016, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", "Unit": "W", + "Version": "000", }, "TypeName": "egauge.io", - "Version": "000", - } + "Version": "001", + }, ], + "HwUid": "BP01349", + "ModbusHost": "eGauge6069.local", + "ModbusPort": 502, "TypeName": "electric.meter.component.gt", - "Version": "000", + "Version": "001", } assert_component_load( [ diff --git a/tests/types/test_multipurpose_sensor_cac_gt.py b/tests/types/test_multipurpose_sensor_cac_gt.py deleted file mode 100644 index 884a019f..00000000 --- a/tests/types/test_multipurpose_sensor_cac_gt.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests multipurpose.sensor.cac.gt type, version 000""" - -from gwproto.types import MultipurposeSensorCacGt -from tests.cac_load_utils import CacCase, assert_cac_load - - -def test_multipurpose_sensor_cac_gt_load() -> None: - d = { - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "MakeModel": "GRIDWORKS__MULTITEMP1", - "PollPeriodMs": 880, - "Exponent": -3, - "TempUnit": "Celcius", - "TelemetryNameList": ["WaterTempCTimes1000"], - "MaxThermistors": 12, - "DisplayName": "Simulated GridWorks high precision water temp sensor", - "CommsMethod": "I2C", - "TypeName": "multipurpose.sensor.cac.gt", - "Version": "000", - } - assert_cac_load([CacCase("MultipurposeSensorCacGt", d, MultipurposeSensorCacGt)]) diff --git a/tests/types/test_multipurpose_sensor_component_gt.py b/tests/types/test_multipurpose_sensor_component_gt.py deleted file mode 100644 index 581c3530..00000000 --- a/tests/types/test_multipurpose_sensor_component_gt.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests multipurpose.sensor.component.gt type, version 000""" - -from gwproto.data_classes.components import MultipurposeSensorComponent -from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt -from tests.component_load_utils import ComponentCase, assert_component_load - - -def test_multipurpose_sensor_component_gt_generated() -> None: - d = { - "ComponentId": "2ca9e65a-5e85-4eaa-811b-901e940f8d09", - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "ChannelList": [1], - "ConfigList": [ - { - "ReportOnChange": False, - "Exponent": 3, - "AboutNodeName": "hp-ewt", - "SamplePeriodS": 60, - "TypeName": "telemetry.reporting.config", - "Version": "001", - "Unit": "Celcius", - "TelemetryName": "WaterTempCTimes1000", - } - ], - "HwUid": "a4f", - "DisplayName": "TSnap for Almond", - "TypeName": "multipurpose.sensor.component.gt", - "Version": "000", - } - assert_component_load( - [ - ComponentCase( - "MultipurposeSensorComponentGt", - d, - MultipurposeSensorComponentGt, - MultipurposeSensorComponent, - ) - ] - ) diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index f2db762c..0949deb2 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -1,20 +1,19 @@ """Tests resistive.heater.component.gt type, version 000""" - +from gwproto.types import ResistiveHeaterCacGt, ResistiveHeaterComponentGt from gwproto.data_classes.components import ResistiveHeaterComponent -from gwproto.types import ResistiveHeaterComponentGt from tests.component_load_utils import ComponentCase, assert_component_load - def test_resistive_heater_component_gt_generated() -> None: d = { "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", "DisplayName": "First 4.5 kW boost in tank", "HwUid": "aaaa2222", + "ConfigList": [], "TestedMaxHotMilliOhms": 13714, "TestedMaxColdMilliOhms": 14500, "TypeName": "resistive.heater.component.gt", - "Version": "000", + "Version": "001", } assert_component_load( [ diff --git a/tests/types/test_telemetry_reporting_config.py b/tests/types/test_telemetry_reporting_config.py deleted file mode 100644 index 34fb5ac4..00000000 --- a/tests/types/test_telemetry_reporting_config.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Tests telemetry.reporting.config type, version 000""" - -from gwproto.types import TelemetryReportingConfig - - -def test_telemetry_reporting_config_generated() -> None: - d = { - "TelemetryName": "PowerW", - "AboutNodeName": "hp-idu", - "ReportOnChange": True, - "SamplePeriodS": 300, - "Exponent": 6, - "Unit": "W", - "AsyncReportThreshold": 0.2, - "NameplateMaxValue": 4000, - "TypeName": "telemetry.reporting.config", - "Version": "001", - } - assert TelemetryReportingConfig.model_validate(d).model_dump() == d diff --git a/tests/types/test_thermistor_data_processing_config.py b/tests/types/test_thermistor_data_processing_config.py new file mode 100644 index 00000000..9fc40b1f --- /dev/null +++ b/tests/types/test_thermistor_data_processing_config.py @@ -0,0 +1,39 @@ +"""Tests thermistor.data.processing.config type, version 000""" + +from gwproto.enums import MakeModel, ThermistorDataMethod +from gwproto.types import ThermistorDataProcessingConfig + + +def test_thermistor_data_processing_config_generated() -> None: + d = { + "ChannelName": "hp-ewt", + "TerminalBlockIdx": 4, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "DataProcessingMethod": "SimpleBeta", + "DataProcessingDescription": "using a beta of 3977.", + "TypeName": "thermistor.data.processing.config", + "Version": "000", + } + + d2 = ThermistorDataProcessingConfig.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d + + ###################################### + # Enum related + ###################################### + + assert type(d2["ThermistorMakeModel"]) is str + + d2 = dict(d, ThermistorMakeModel="unknown_enum_thing") + assert ( + ThermistorDataProcessingConfig(**d2).ThermistorMakeModel == MakeModel.default() + ) + + assert type(d2["DataProcessingMethod"]) is str + + d2 = dict(d, DataProcessingMethod="unknown_enum_thing") + assert ( + ThermistorDataProcessingConfig(**d2).DataProcessingMethod + == ThermistorDataMethod.default() + ) From 56f16443b082c3454786cc332a6f1a0727db94da Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:11:16 -0400 Subject: [PATCH 131/168] Update Cacs --- src/gwproto/enums/__init__.py | 2 + src/gwproto/types/__init__.py | 20 +- src/gwproto/types/cacs.py | 4 +- .../types/component_attribute_class_gt.py | 5 +- src/gwproto/types/data_channel_gt.py | 6 +- src/gwproto/types/electric_meter_cac_gt.py | 4 +- tests/config/hardware-layout.json | 680 +++++++++++++++--- tests/data_classes/test_electric_meter_cac.py | 16 - .../test_component_attribute_class_gt.py | 3 +- tests/types/test_component_gt.py | 19 +- tests/types/test_data_channel_gt.py | 2 +- tests/types/test_egauge_io.py | 17 +- tests/types/test_electric_meter_cac_gt.py | 1 + tests/types/test_resistive_heater_cac_gt.py | 3 +- .../test_resistive_heater_component_gt.py | 4 +- tests/types/test_spaceheat_node_gt.py | 15 +- 16 files changed, 636 insertions(+), 165 deletions(-) delete mode 100644 tests/data_classes/test_electric_meter_cac.py diff --git a/src/gwproto/enums/__init__.py b/src/gwproto/enums/__init__.py index 81089efa..595bfbfc 100644 --- a/src/gwproto/enums/__init__.py +++ b/src/gwproto/enums/__init__.py @@ -37,6 +37,7 @@ from gwproto.enums.make_model import MakeModel from gwproto.enums.role import Role from gwproto.enums.telemetry_name import TelemetryName +from gwproto.enums.thermistor_data_method import ThermistorDataMethod from gwproto.enums.unit import Unit __all__ = [ @@ -45,5 +46,6 @@ "MakeModel", # [spaceheat.make.model.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel) "Role", # [sh.node.role.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole) "TelemetryName", # [spaceheat.telemetry.name.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname) + "ThermistorDataMethod", "Unit", # [spaceheat.unit.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit) ] diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index ddac62d3..dc3d0a17 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -1,5 +1,8 @@ """List of all the types""" +from gwproto.types.ads111x_based_cac_gt import Ads111xBasedCacGt +from gwproto.types.ads111x_based_component_gt import Ads111xBasedComponentGt +from gwproto.types.channel_config import ChannelConfig from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt from gwproto.types.data_channel_gt import DataChannelGt @@ -36,10 +39,6 @@ from gwproto.types.hubitat_tank_component_gt import ( HubitatTankComponentGt, ) -from gwproto.types.multipurpose_sensor_cac_gt import ( - MultipurposeSensorCacGt, -) -from gwproto.types.multipurpose_sensor_component_gt import MultipurposeSensorComponentGt from gwproto.types.power_watts import PowerWatts from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, @@ -52,14 +51,17 @@ ) from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt -from gwproto.types.telemetry_reporting_config import ( - TelemetryReportingConfig, -) from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, ) +from gwproto.types.thermistor_data_processing_config import ( + ThermistorDataProcessingConfig, +) __all__ = [ + "Ads111xBasedCacGt", + "Ads111xBasedComponentGt", + "ChannelConfig", "ComponentAttributeClassGt", "ComponentGt", "DataChannelGt", @@ -78,16 +80,14 @@ "HubitatComponentGt", "HubitatPollerComponentGt", "HubitatTankComponentGt", - "MultipurposeSensorCacGt", - "MultipurposeSensorComponentGt", "PowerWatts", "RESTPollerComponentGt", "ResistiveHeaterCacGt", "ResistiveHeaterComponentGt", "SnapshotSpaceheat", "SpaceheatNodeGt", - "TelemetryReportingConfig", "TelemetrySnapshotSpaceheat", + "ThermistorDataProcessingConfig", "cacs", # noqa: F822 "components", # noqa: F822 ] diff --git a/src/gwproto/types/cacs.py b/src/gwproto/types/cacs.py index 691e3373..bc83bbfb 100644 --- a/src/gwproto/types/cacs.py +++ b/src/gwproto/types/cacs.py @@ -1,9 +1,9 @@ +from gwproto.types.ads111x_based_cac_gt import Ads111xBasedCacGt from gwproto.types.electric_meter_cac_gt import ElectricMeterCacGt -from gwproto.types.multipurpose_sensor_cac_gt import MultipurposeSensorCacGt from gwproto.types.resistive_heater_cac_gt import ResistiveHeaterCacGt __all__ = [ + "Ads111xBasedCacGt", "ElectricMeterCacGt", - "MultipurposeSensorCacGt", "ResistiveHeaterCacGt", ] diff --git a/src/gwproto/types/component_attribute_class_gt.py b/src/gwproto/types/component_attribute_class_gt.py index aa1d5286..7d3ba678 100644 --- a/src/gwproto/types/component_attribute_class_gt.py +++ b/src/gwproto/types/component_attribute_class_gt.py @@ -2,7 +2,7 @@ from typing import Literal, Optional -from pydantic import BaseModel, ConfigDict, model_validator +from pydantic import BaseModel, ConfigDict, PositiveInt, model_validator from typing_extensions import Self from gwproto.enums import MakeModel @@ -14,8 +14,9 @@ class ComponentAttributeClassGt(BaseModel): ComponentAttributeClassId: UUID4Str DisplayName: Optional[str] = None MakeModel: MakeModel + MinPollPeriodMs: Optional[PositiveInt] = None TypeName: Literal["component.attribute.class.gt"] = "component.attribute.class.gt" - Version: Literal["000"] = "000" + Version: Literal["001"] = "001" model_config = ConfigDict(use_enum_values=True, extra="allow") diff --git a/src/gwproto/types/data_channel_gt.py b/src/gwproto/types/data_channel_gt.py index 8395a5df..a62f92f1 100644 --- a/src/gwproto/types/data_channel_gt.py +++ b/src/gwproto/types/data_channel_gt.py @@ -33,7 +33,11 @@ def check_axiom_1(self) -> Self: Axiom 1: Power Metering. If InPowerMetering is true then the TelemetryName must be PowerW """ - # Implement check for axiom 1" + if self.InPowerMetering and self.TelemetryName != TelemetryName.PowerW: + raise ValueError( + "Axiom 1 violated! If InPowerMetering is true then" + f"the TelemetryName must be PowerW. Got {self.TelemetryName}" + ) return self model_config = ConfigDict(use_enum_values=True) diff --git a/src/gwproto/types/electric_meter_cac_gt.py b/src/gwproto/types/electric_meter_cac_gt.py index 63c44d45..f63ed43d 100644 --- a/src/gwproto/types/electric_meter_cac_gt.py +++ b/src/gwproto/types/electric_meter_cac_gt.py @@ -2,13 +2,11 @@ from typing import Literal, Optional -from gwproto.enums import LocalCommInterface, TelemetryName +from gwproto.enums import TelemetryName from gwproto.types import ComponentAttributeClassGt class ElectricMeterCacGt(ComponentAttributeClassGt): TelemetryNameList: list[TelemetryName] - PollPeriodMs: int - Interface: LocalCommInterface DefaultBaud: Optional[int] = None TypeName: Literal["electric.meter.cac.gt"] = "electric.meter.cac.gt" diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 9e967080..bd9d4cff 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -4,18 +4,17 @@ "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", "MakeModel": "GRIDWORKS__SIMPM1", "DisplayName": "Gridworks Pm1 Simulated Power Meter", - "Interface": "SIMRABBIT", - "PollPeriodMs": 1000, + "MinPollPeriodMs": 1000, "TelemetryNameList": ["PowerW"], "TypeName": "electric.meter.cac.gt", - "Version": "000" + "Version": "001" }, { "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", "DisplayName": "EGauge 4030", "MakeModel": "EGAUGE__4030", - "Interface": "ETHERNET", - "PollPeriodMs": 1000, + "MinPollPeriodMs": 1000, + "DefaultBaud": 9600, "TelemetryNameList": [ "PowerW", "MilliWattHours", @@ -24,7 +23,7 @@ "FrequencyMicroHz" ], "TypeName": "electric.meter.cac.gt", - "Version": "000" + "Version": "001" } ], "ElectricMeterComponents": [ @@ -34,78 +33,325 @@ "DisplayName": "Power Meter for Simulated Test system", "ConfigList": [ { - "AboutNodeName": "elt1", - "ReportOnChange": true, - "SamplePeriodS": 300, + "AsyncCapture": true, + "AsyncCaptureDelta": 200, + "CapturePeriodS": 300, + "ChannelName": "hp-odu-pwr", "Exponent": 0, - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "001", - "TelemetryName": "Power", - "Unit": "W" - } + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 200, + "CapturePeriodS": 300, + "ChannelName": "hp-idu-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 5, + "CapturePeriodS": 300, + "ChannelName": "store-pump-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 200, + "CapturePeriodS": 300, + "ChannelName": "elt1-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "W", + "Version": "000" + } ], "HwUid": "1001ab", - "EgaugeIoList": [], + "EgaugeIoList": [ + { + "ChannelName": "hp-odu-pwr", + "InputConfig": { + "Address": 9000, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "egauge.io", + "Version": "001" + }, + { + "ChannelName": "hp-odu-pwr", + "InputConfig": { + "Address": 9002, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "egauge.io", + "Version": "001" + }, + { + "ChannelName": "store-pump-pwr", + "InputConfig": { + "Address": 9014, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "egauge.io", + "Version": "001" + }, + { + "ChannelName": "elt1-pwr", + "InputConfig": { + "Address": 9006, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "egauge.io", + "Version": "001" + } + ], + "ModbusHost": "eGauge6069.local", + "ModbusPort": 502, "TypeName": "electric.meter.component.gt", - "Version": "000" + "Version": "001" } ], - "MultipurposeSensorCacs": [ + "Ads111xBasedCacs": [ { "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "MakeModel": "GRIDWORKS__MULTITEMP1", - "PollPeriodMs": 200, - "Exponent": -3, - "TempUnit": "Celcius", - "TelemetryNameList": ["WaterTempCTimes1000"], - "MaxThermistors": 12, - "DisplayName": "Simulated GridWorks high precision water temp sensor", - "CommsMethod": "I2C", - "TypeName": "multipurpose.sensor.cac.gt", - "Version": "000" - } + "MinPollPeriodMs": 200, + "MakeModel": "GRIDWORKS__MULTITEMP1", + "AdsI2cAddressList": ["0x4b", "0x48", "0x49"], + "TotalTerminalBlocks": 12, + "TelemetryNameList": ["WaterTempCTimes1000", "AirTempCTimes1000"], + "DisplayName": "Gridworks 12-channel MultiTemp Ads Sensor", + "TypeName": "ads111x.based.cac.gt", + "Version": "000" + } ], - "MultipurposeSensorComponents": [ + "Ads111xBasedComponents": [ { - "ChannelList": [ - 3, - 4 - ], "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "ComponentId": "fe18bffa-4b46-4b03-89d4-26b3d6f3e9d1", + "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", "ConfigList": [ - { - "AboutNodeName": "hp-lwt", - "AsyncReportThreshold": 0.0025, - "Exponent": 3, - "NameplateMaxValue": 100000, - "ReportOnChange": true, - "SamplePeriodS": 60, - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "telemetry.reporting.config", - "Unit": "Celsius", - "Version": "001" - }, - { - "AboutNodeName": "hp-ewt", - "AsyncReportThreshold": 0.0025, - "Exponent": 3, - "NameplateMaxValue": 100000, - "ReportOnChange": true, - "SamplePeriodS": 60, - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "telemetry.reporting.config", - "Unit": "Celsius", - "Version": "001" - } + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-swt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-rwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-lwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-ewt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "ChannelName": "oat", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + } ], - "DisplayName": "Multipurpose Temp Sensor Component <100>", - "HwUid": "100", - "TypeName": "multipurpose.sensor.component.gt", + "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", + "OpenVoltageByAds": [4.95, 4.95, 4.95], + "ThermistorConfigList": [ + { + "ChannelName": "dist-swt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 1, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "dist-rwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 2, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "hp-lwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 3, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "hp-ewt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 4, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "store-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 5, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "store-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 6, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "buffer-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 7, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "buffer-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 8, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "oat", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 9, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + } + ], + "TypeName": "ads111x.based.component.gt", "Version": "000" - } + } ], "MyAtomicTNodeGNode": { "GNodeId": "c4c9a280-453f-4c36-a081-970a3774b3ed", @@ -134,8 +380,9 @@ "DisplayName": "First 4.5 kW boost in tank", "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", "TypeName": "resistive.heater.component.gt", - "Version": "000", + "Version": "001", "HwUid": "aaaa2222", + "ConfigList": [], "TestedMaxColdMilliOhms": 14500, "TestedMaxHotMilliOhms": 13714 }, @@ -143,8 +390,9 @@ "ComponentId": "d5fbf9f4-18a5-48f8-abdf-919309424321", "DisplayName": "Second 4.5 kW boost in tank", "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", + "ConfigList": [], "TypeName": "resistive.heater.component.gt", - "Version": "000", + "Version": "001", "HwUid": "bbbb2222" } ], @@ -156,46 +404,51 @@ "DisplayName": "Fake Boost Element", "NameplateMaxPowerW": 4500, "RatedVoltageV": 240, + "MinPollPeriodMs": 1000, "TypeName": "resistive.heater.cac.gt", - "Version": "000" + "Version": "001" } ], "OtherCacs": [ { "ComponentAttributeClassId": "56498ea1-64b8-4102-b76f-4e29555d4d60", "DisplayName": "Web Server CAC", + "MinPollPeriomdMs": 1000, "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "TypeName": "component.attribute.class.gt", - "Version": "000" + "Version": "001" }, { "ComponentAttributeClassId": "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", "DisplayName": "Fibaro SmartImplant FGBS-222", + "MinPollPeriomdMs": 1000, "MakeModel": "FIBARO__ANALOG_TEMP_SENSOR", - "Model": "FGBS-222 v5.2", "TypeName": "component.attribute.class.gt", - "Version": "000" + "Version": "001" }, { "ComponentAttributeClassId": "62528da5-b510-4ac2-82c1-3782842eae07", "DisplayName": "Hubitat Elevation C-7", "MakeModel": "HUBITAT__C7__LAN1", + "MinPollPeriomdMs": 1000, "TypeName": "component.attribute.class.gt", - "Version": "000" + "Version": "001" }, { "ComponentAttributeClassId": "60ac199d-679a-49f7-9142-8ca3e6428a5f", "DisplayName": "Hubitat Tank Module", "MakeModel": "GRIDWORKS__TANK_MODULE_1", + "MinPollPeriomdMs": 1000, "TypeName": "component.attribute.class.gt", - "Version": "000" + "Version": "001" }, { "ComponentAttributeClassId": "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", "DisplayName": "Honeywell Z-Wave T6 Thermostat", "MakeModel": "HONEYWELL__T6ZWAVETHERMOSTAT", + "MinPollPeriomdMs": 1000, "TypeName": "component.attribute.class.gt", - "Version": "000" + "Version": "001" } ], "ShNodes": [ @@ -206,7 +459,7 @@ "DisplayName": "AtomicTNode", "ShNodeId": "b354edeb-0c82-4e55-80cb-7ab669ac2ad9", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" }, { "Alias": "home", @@ -215,7 +468,7 @@ "DisplayName": "Little Orange House HomeAlone", "ShNodeId": "34470c9d-fa25-4077-909b-2f981a691d7e", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" }, { "Alias": "s", @@ -224,7 +477,7 @@ "DisplayName": "Little Orange House Main Scada", "ShNodeId": "259f9431-c6a1-4170-8766-04cbf65cff4a", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" }, { "Alias": "elt1", @@ -233,11 +486,10 @@ "DisplayName": "First boost element", "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", - "RatedVoltageV": 240, - "TypicalVoltageV": 225, "InPowerMetering": true, + "NameplatePowerW": 4500, "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" }, { "Alias": "power-meter", @@ -247,7 +499,43 @@ "ShNodeId": "0dd8a803-4724-4f49-b845-14ff57bdb3e6", "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" + }, + { + "Alias": "buffer-cold-pipe", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Buffer Cold Pipe", + "ShNodeId": "d2aa5af6-2cc7-4067-bd70-251c65b86a34", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "buffer-hot-pipe", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Buffer Hot Pipe", + "ShNodeId": "d64e2fa3-5ca2-4665-b60a-253323455f5a", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "dist-rwt", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Dist Return Water Temp", + "ShNodeId": "dc8a3e92-e09b-4976-bc32-c7f2dae22dc5", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "dist-swt", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Dist Source Water Temp", + "ShNodeId": "dfb1c311-f2db-4752-be11-ac0d4d91d71b", + "TypeName": "spaceheat.node.gt", + "Version": "120" }, { "Alias": "hp-ewt", @@ -256,7 +544,7 @@ "DisplayName": "Heat Pump Entering Water Temp", "ShNodeId": "e76073f4-67ae-4324-b07f-0c9add733de7", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" }, { "Alias": "hp-lwt", @@ -265,7 +553,34 @@ "DisplayName": "Heat Pump Leaving Water Temp", "ShNodeId": "1fe2a01f-d60a-4362-bc5e-949ddfcd5003", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" + }, + { + "Alias": "oat", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Store Cold Pipe", + "ShNodeId": "7ef9d1af-5f6c-430e-9d03-c00e480fcd9a", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "store-cold-pipe", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Store Cold Pipe", + "ShNodeId": "e0e9b848-a593-465d-b9df-9d42c16cfffa", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "store-hot-pipe", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Store Hot Pipe", + "ShNodeId": "26b31718-feec-446e-ba1e-c921d760f1dd", + "TypeName": "spaceheat.node.gt", + "Version": "120" }, { "Alias": "store-pump", @@ -274,7 +589,29 @@ "DisplayName": "Store Pump", "ShNodeId": "c35d9c77-1ebb-4822-b01e-00ff189092f7", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" + }, + { + "Alias": "hp-idu", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "HP IDU", + "InPowerMetering": true, + "NameplatePowerW": 3500, + "ShNodeId": "7da56e55-3cf0-4a2f-9c06-0c6c176b795c", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, + { + "Alias": "hp-odu", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "HP ODU", + "InPowerMetering": true, + "NameplatePowerW": 6500, + "ShNodeId": "f1692ea8-9866-423c-b260-93d2affc9881", + "TypeName": "spaceheat.node.gt", + "Version": "120" }, { "Alias": "analog-temp", @@ -283,33 +620,160 @@ "DisplayName": "GridWorks MultiTemp", "ShNodeId": "8bfc40f1-7c84-4e88-9214-78c304730ae3", "TypeName": "spaceheat.node.gt", - "Version": "110" + "Version": "120" } ], "DataChannels": [ - { - "Name": "store-pump-pwr", - "DisplayName": "Store pump power", - "AboutNodeName": "store-pump", - "CapturedByNodeName": "power-meter", - "TelemetryName": "PowerW", - "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", - "StartS": 1701293980, - "Id": "ac35c2a9-e317-45e8-a036-52fa5cbd8380", - "TypeName": "data.channel.gt", - "Version": "001" - }, - { - "Name": "hp-lwt", - "DisplayName": "Heat Pump Leaving Water Temp", - "AboutNodeName": "hp-lwt", - "CapturedByNodeName": "analog-temp", - "TelemetryName": "WaterTempCTimes1000", - "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", - "StartS": 1701293980, - "Id": "ac35c2a9-e317-45e8-a036-52fa5cbd8380", - "TypeName": "data.channel.gt", - "Version": "001" - } + { + "Name": "buffer-cold-pipe", + "DisplayName": "Buffer Cold Pipe (C x 1000)", + "AboutNodeName": "buffer-cold-pipe", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "a10940de-b5c7-4373-8787-d8793b0b2d8f", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "buffer-hot-pipe", + "DisplayName": "Buffer Hot Pipe (C x 1000)", + "AboutNodeName": "buffer-hot-pipe", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "9310c8fe-ebd5-4fa9-a97f-a8bccee143a1", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "dist-rwt", + "DisplayName": "Dist RWT(C x 1000)", + "AboutNodeName": "dist-rwt", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "a2b250cd-c8b2-4651-a83e-aafe933d0ccd", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "dist-swt", + "DisplayName": "Dist SWT(C x 1000)", + "AboutNodeName": "dist-swt", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "50f78516-30eb-44c5-a2cf-396a973a8569", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "hp-ewt", + "DisplayName": "Heat Pump EWT(C x 1000)", + "AboutNodeName": "hp-ewt", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "0b01f7cd-bed1-478f-b3ae-03395b856eb6", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "hp-lwt", + "DisplayName": "Heat Pump Leaving Water Temp", + "AboutNodeName": "hp-lwt", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "Id": "ac35c2a9-e317-45e8-a036-52fa5cbd8380", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "store-cold-pipe", + "DisplayName": "Store Cold Pipe (C x 1000)", + "AboutNodeName": "store-cold-pipe", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "766077c5-57d9-4117-ac0b-22c4b231b391", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "store-hot-pipe", + "DisplayName": "Store Hot Pipe (C x 1000)", + "AboutNodeName": "store-hot-pipe", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "ec27e1cb-a0b9-424f-8279-894b0db5ee62", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "oat", + "DisplayName": "Outside Air Temp (C x 1000)", + "AboutNodeName": "oat", + "CapturedByNodeName": "analog-temp", + "TelemetryName": "WaterTempCTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "9439ca3a-9e40-46bb-99c0-674bbb3c914a", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "hp-odu-pwr", + "DisplayName": "HP ODU", + "AboutNodeName": "hp-odu", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "InPowerMetering": true, + "Id": "4cb6a9e6-24c4-4b72-9a7d-af5b884c83bd", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "hp-idu-pwr", + "DisplayName": "HP IDU", + "AboutNodeName": "hp-idu", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "InPowerMetering": true, + "Id": "5ac27781-2976-49c4-8890-0d0fdad219f7", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "elt1-pwr", + "DisplayName": "Elt1 Power", + "AboutNodeName": "elt1", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "InPowerMetering": true, + "Id": "f97d6920-3a9c-4d52-8ddb-fd89f16e1935", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "store-pump-pwr", + "DisplayName": "Store pump power", + "AboutNodeName": "store-pump", + "CapturedByNodeName": "power-meter", + "TelemetryName": "PowerW", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "StartS": 1701293980, + "Id": "7876ebb2-03d2-4c7f-9046-011b7c5d4deb", + "TypeName": "data.channel.gt", + "Version": "001" + } ] } diff --git a/tests/data_classes/test_electric_meter_cac.py b/tests/data_classes/test_electric_meter_cac.py deleted file mode 100644 index 02d13da4..00000000 --- a/tests/data_classes/test_electric_meter_cac.py +++ /dev/null @@ -1,16 +0,0 @@ -from gwproto.types import ElectricMeterCacGt -from tests.cac_load_utils import CacCase, assert_cac_load - - -def test_electric_meter_cac() -> None: - d = { - "ComponentAttributeClassId": "28897ac1-ea42-4633-96d3-196f63f5a951", - "MakeModel": "GRIDWORKS__SIMPM1", - "DisplayName": "Gridworks Pm1 Simulated Power Meter", - "Interface": "SIMRABBIT", - "PollPeriodMs": 1000, - "TelemetryNameList": ["PowerW"], - "TypeName": "electric.meter.cac.gt", - "Version": "000", - } - assert_cac_load([CacCase("ElectricMeterCac", d, ElectricMeterCacGt)]) diff --git a/tests/types/test_component_attribute_class_gt.py b/tests/types/test_component_attribute_class_gt.py index b570e118..15de7bcf 100644 --- a/tests/types/test_component_attribute_class_gt.py +++ b/tests/types/test_component_attribute_class_gt.py @@ -14,8 +14,9 @@ def test_component_attribute_class_gt_load() -> None: "ComponentAttributeClassId": "e52cb571-913a-4614-90f4-5cc81f8e7fe5", "MakeModel": "EKM__HOTSPWM075HD", "DisplayName": "EKM Hot-Spwm-075-HD Flow Meter", + "MinPollPeriodMs": 1000, "TypeName": "component.attribute.class.gt", - "Version": "000", + "Version": "001", } assert_cac_load( [CacCase("ComponentAttributeClassGt", d, ComponentAttributeClassGt)] diff --git a/tests/types/test_component_gt.py b/tests/types/test_component_gt.py index 87b18457..2d247e29 100644 --- a/tests/types/test_component_gt.py +++ b/tests/types/test_component_gt.py @@ -8,11 +8,24 @@ def test_component_gt_generated() -> None: d = { "ComponentId": "c6ec1ddb-5f51-4902-9807-a5ebc74d1102", - "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", - "DisplayName": "Demo eGauge Power Meter", + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "ConfigList": [ + { + "ChannelName": "store-cold-pipe", + "PollPeriodMs": 1000, + "CapturePeriodS": 60, + "AsyncCapture": True, + "AsyncCaptureDelta": 30, + "Exponent": 3, + "Unit": "Celcius", + "TypeName": "channel.config", + "Version": "000", + } + ], + "DisplayName": "MultiTemp", "HwUid": "000aaa", "TypeName": "component.gt", - "Version": "000", + "Version": "001", } assert_component_load([ComponentCase("ComponentGt", d, ComponentGt, Component)]) diff --git a/tests/types/test_data_channel_gt.py b/tests/types/test_data_channel_gt.py index c7ea4e25..5cd1ee1d 100644 --- a/tests/types/test_data_channel_gt.py +++ b/tests/types/test_data_channel_gt.py @@ -29,5 +29,5 @@ def test_data_channel_gt_generated() -> None: assert type(d2["TelemetryName"]) is str - d2 = dict(d, TelemetryName="unknown_enum_thing") + d2 = dict(d, TelemetryName="unknown_enum_thing", InPowerMetering=False) assert DataChannelGt(**d2).TelemetryName == TelemetryName.default() diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py index 3cad137c..6c328c23 100644 --- a/tests/types/test_egauge_io.py +++ b/tests/types/test_egauge_io.py @@ -5,9 +5,10 @@ def test_egauge_io_generated() -> None: d = { + "ChannelName": "hp-idu-pwr", "InputConfig": { "Address": 9004, - "Name": "HP IDU Power", + "Name": "Garage power", "Description": "", "Type": "f32", "Denominator": 1, @@ -15,19 +16,7 @@ def test_egauge_io_generated() -> None: "TypeName": "egauge.register.config", "Version": "000", }, - "OutputConfig": { - "AboutNodeName": "hp-idu", - "ReportOnChange": True, - "SamplePeriodS": 60, - "Exponent": 0, - "AsyncReportThreshold": 0.05, - "NameplateMaxValue": 4500, - "TypeName": "telemetry.reporting.config", - "Version": "001", - "TelemetryName": "PowerW", - "Unit": "W", - }, "TypeName": "egauge.io", - "Version": "000", + "Version": "001", } assert EgaugeIo.model_validate(d).model_dump() == d diff --git a/tests/types/test_electric_meter_cac_gt.py b/tests/types/test_electric_meter_cac_gt.py index a0cfe472..5ad3208b 100644 --- a/tests/types/test_electric_meter_cac_gt.py +++ b/tests/types/test_electric_meter_cac_gt.py @@ -1,4 +1,5 @@ """Tests electric.meter.cac.gt type, version 000""" + import pytest from pydantic import ValidationError diff --git a/tests/types/test_resistive_heater_cac_gt.py b/tests/types/test_resistive_heater_cac_gt.py index 4eae9ce0..d40b2606 100644 --- a/tests/types/test_resistive_heater_cac_gt.py +++ b/tests/types/test_resistive_heater_cac_gt.py @@ -10,9 +10,10 @@ def test_resistive_heater_cac_gt_load() -> None: # "MakeModelGtEnumSymbol": "00000000", "MakeModel": "UNKNOWNMAKE__UNKNOWNMODEL", "DisplayName": "Fake Boost Element", + "MinPollPeriodMs": 1000, "NameplateMaxPowerW": 4500, "RatedVoltageV": 240, "TypeName": "resistive.heater.cac.gt", - "Version": "000", + "Version": "001", } assert_cac_load([CacCase("ResistiveHeaterCacGt", d, ResistiveHeaterCacGt)]) diff --git a/tests/types/test_resistive_heater_component_gt.py b/tests/types/test_resistive_heater_component_gt.py index 0949deb2..85d3d119 100644 --- a/tests/types/test_resistive_heater_component_gt.py +++ b/tests/types/test_resistive_heater_component_gt.py @@ -1,8 +1,10 @@ """Tests resistive.heater.component.gt type, version 000""" -from gwproto.types import ResistiveHeaterCacGt, ResistiveHeaterComponentGt + from gwproto.data_classes.components import ResistiveHeaterComponent +from gwproto.types import ResistiveHeaterComponentGt from tests.component_load_utils import ComponentCase, assert_component_load + def test_resistive_heater_component_gt_generated() -> None: d = { "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 60c5711a..4b56d6d7 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -1,5 +1,8 @@ """Tests spaceheat.node.gt type, version 100""" +import pytest +from pydantic import ValidationError + from gwproto.types import SpaceheatNodeGt @@ -14,6 +17,14 @@ def test_spaceheat_node_gt_generated() -> None: "ReportingSamplePeriodS": 300, "InPowerMetering": False, "TypeName": "spaceheat.node.gt", - "Version": "110", + "Version": "120", } - assert SpaceheatNodeGt.model_validate(d).model_dump() == d + assert SpaceheatNodeGt.model_validate(d).model_dump(exclude_none=True) == d + + d2 = dict( + d, + InPowerMetering="True", + ) + # testing axiom 1: If InPowerMetering exists and is true, then NameplatePowerW must exist + with pytest.raises(ValidationError): + SpaceheatNodeGt.model_validate(d2) From c5d6f529b5069eef22d7ff7e563a3b1a7b232d53 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:12:13 -0400 Subject: [PATCH 132/168] remove config_list as property for hubitat dcs --- src/gwproto/data_classes/components/__init__.py | 7 ++++--- src/gwproto/data_classes/components/component.py | 2 +- .../components/hubitat_poller_component.py | 15 --------------- .../components/hubitat_tank_component.py | 15 --------------- 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/gwproto/data_classes/components/__init__.py b/src/gwproto/data_classes/components/__init__.py index b1baf8e7..85711a56 100644 --- a/src/gwproto/data_classes/components/__init__.py +++ b/src/gwproto/data_classes/components/__init__.py @@ -1,3 +1,6 @@ +from gwproto.data_classes.components.ads111x_based_component import ( + Ads111xBasedComponent, +) from gwproto.data_classes.components.component import Component from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, @@ -10,9 +13,6 @@ HubitatPollerComponent, ) from gwproto.data_classes.components.hubitat_tank_component import HubitatTankComponent -from gwproto.data_classes.components.multipurpose_sensor_component import ( - MultipurposeSensorComponent, -) from gwproto.data_classes.components.resistive_heater_component import ( ResistiveHeaterComponent, ) @@ -21,6 +21,7 @@ __all__ = [ "Component", + "Ads111xBasedComponent", "ElectricMeterComponent", "FibaroSmartImplantComponent", "HubitatComponent", diff --git a/src/gwproto/data_classes/components/component.py b/src/gwproto/data_classes/components/component.py index 3f6dbd63..8ce1f2fd 100644 --- a/src/gwproto/data_classes/components/component.py +++ b/src/gwproto/data_classes/components/component.py @@ -17,7 +17,7 @@ def __init__(self, gt: ComponentT, cac: CacT) -> None: self.cac = cac def __repr__(self) -> str: - return f"<{self.gt.DisplayName}> ({self.cac.MakeModel.value})" + return f"<{self.gt.DisplayName}> ({self.cac.MakeModel})" class ComponentOnly(Component[ComponentGt, ComponentAttributeClassGt]): ... diff --git a/src/gwproto/data_classes/components/hubitat_poller_component.py b/src/gwproto/data_classes/components/hubitat_poller_component.py index bb63e897..531f283e 100644 --- a/src/gwproto/data_classes/components/hubitat_poller_component.py +++ b/src/gwproto/data_classes/components/hubitat_poller_component.py @@ -10,7 +10,6 @@ from gwproto.types.hubitat_component_gt import HubitatComponentGt from gwproto.types.hubitat_poller_component_gt import HubitatPollerComponentGt from gwproto.types.rest_poller_gt import RequestArgs, RESTPollerSettings -from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig class HubitatPollerComponent( @@ -79,17 +78,3 @@ def urls(self) -> dict[str, Optional[yarl.URL]]: for attribute in self.gt.Poller.attributes: urls[attribute.node_name] = self.rest.url return urls - - @property - def config_list(self) -> list[TelemetryReportingConfig]: - return [ - TelemetryReportingConfig( - TelemetryName=attribute.telemetry_name, - AboutNodeName=attribute.node_name, - ReportOnChange=False, - SamplePeriodS=int(self.rest.poll_period_seconds), - Exponent=attribute.exponent, - Unit=attribute.unit, - ) - for attribute in self.gt.Poller.attributes - ] diff --git a/src/gwproto/data_classes/components/hubitat_tank_component.py b/src/gwproto/data_classes/components/hubitat_tank_component.py index 92debede..655bf588 100644 --- a/src/gwproto/data_classes/components/hubitat_tank_component.py +++ b/src/gwproto/data_classes/components/hubitat_tank_component.py @@ -16,7 +16,6 @@ FibaroTempSensorSettings, FibaroTempSensorSettingsGt, ) -from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig class HubitatTankComponent( @@ -96,17 +95,3 @@ def urls(self) -> dict[str, Optional[yarl.URL]]: for device in self.devices: urls[device.node_name] = device.url return urls - - @property - def config_list(self) -> list[TelemetryReportingConfig]: - return [ - TelemetryReportingConfig( - TelemetryName=device.telemetry_name, - AboutNodeName=device.node_name, - ReportOnChange=False, - SamplePeriodS=int(device.rest.poll_period_seconds), - Exponent=device.exponent, - Unit=device.unit, - ) - for device in self.devices - ] From f6a2082a1f678d3a1ae9311eea3c723a94436a34 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:13:31 -0400 Subject: [PATCH 133/168] forgot a cac. type enum fields don't have values --- src/gwproto/data_classes/sh_node.py | 2 +- src/gwproto/types/ads111x_based_cac_gt.py | 50 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/gwproto/types/ads111x_based_cac_gt.py diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 1b9bdf39..772b791a 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -57,7 +57,7 @@ def in_power_metering(self) -> Optional[bool]: return self.InPowerMetering def __repr__(self) -> str: - rs = f"ShNode {self.display_name} => {self.role.value} {self.alias}, " + rs = f"ShNode {self.display_name} => {self.role} {self.alias}, " if self.has_actor: rs += " (has actor)" else: diff --git a/src/gwproto/types/ads111x_based_cac_gt.py b/src/gwproto/types/ads111x_based_cac_gt.py new file mode 100644 index 00000000..a263b4eb --- /dev/null +++ b/src/gwproto/types/ads111x_based_cac_gt.py @@ -0,0 +1,50 @@ +"""Type ads111x.based.cac.gt, version 000""" + +from typing import List, Literal + +from pydantic import ( + ConfigDict, + PositiveInt, + field_validator, + model_validator, +) +from typing_extensions import Self + +from gwproto.enums import TelemetryName +from gwproto.property_format import ( + check_is_ads1115_i2c_address, +) +from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt + + +class Ads111xBasedCacGt(ComponentAttributeClassGt): + AdsI2cAddressList: List[str] + TotalTerminalBlocks: PositiveInt + TelemetryNameList: List[TelemetryName] + TypeName: Literal["ads111x.based.cac.gt"] = "ads111x.based.cac.gt" + Version: Literal["000"] = "000" + + model_config = ConfigDict(extra="allow", use_enum_values=True) + + @field_validator("AdsI2cAddressList") + @classmethod + def _check_ads_i2c_address_list(cls, v: List[str]) -> List[str]: + try: + for elt in v: + check_is_ads1115_i2c_address(elt) + except ValueError as e: + raise ValueError( + f"AdsI2cAddressList element failed Ads1115I2cAddress format validation: {e}", + ) from e + return v + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: TerminalBlock Ads Chip consistency. + TotalTerminalBlocks should be greater than 4 * (len(AdsI2cAddressList) - 1 ) and less than or equal to 4*len(AdsI2cAddressList) + """ + # Implement check for axiom 1" + return self + + From 1f99f603f114f93f7742a409f8c0a60e9fa3e8c6 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:15:07 -0400 Subject: [PATCH 134/168] A lot more consistency checks in hardware layout. Add axiom for spaceheat node --- src/gwproto/data_classes/hardware_layout.py | 116 +++++++++++++++++--- src/gwproto/types/spaceheat_node_gt.py | 21 +++- 2 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index bec9e7d9..0b101085 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -8,12 +8,14 @@ import copy import json import typing -from collections import defaultdict +from collections import Counter, defaultdict from dataclasses import dataclass from functools import cached_property from pathlib import Path from typing import Any, List, Optional, Type, TypeVar +from gw.errors import DcError + import gwproto.data_classes.components from gwproto.data_classes.components import Component from gwproto.data_classes.components.component import ComponentOnly @@ -21,7 +23,6 @@ ElectricMeterComponent, ) from gwproto.data_classes.data_channel import DataChannel -from gwproto.data_classes.errors import DataClassLoadingError from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode from gwproto.data_classes.telemetry_tuple import TelemetryTuple @@ -74,9 +75,9 @@ def load_cacs( cac_decoder = default_cac_decoder cacs: dict[str, ComponentAttributeClassGt] = {} for type_name in [ + "Ads111xBasedCacs", "ResistiveHeaterCacs", "ElectricMeterCacs", - "MultipurposeSensorCacs", "OtherCacs", ]: for cac_dict in layout.get(type_name, ()): @@ -95,7 +96,7 @@ def get_data_class_name(cls, component_gt: ComponentGt) -> str: if not gt_class_name.endswith(cls.GT_SUFFIX) or len(gt_class_name) <= len( cls.GT_SUFFIX ): - raise DataClassLoadingError( # noqa: TRY301 + raise DcError( # noqa: TRY301 f"Name of decoded component class ({gt_class_name}) " f"must end with <{cls.GT_SUFFIX}> " f"and be longer than {len(cls.GT_SUFFIX)} chars" @@ -115,7 +116,7 @@ def make_component( cls, component_gt: ComponentGt, cac: ComponentAttributeClassGt ) -> Component: if cac is None: - raise DataClassLoadingError( # noqa: TRY301 + raise DcError( # noqa: TRY301 f"cac {component_gt.ComponentAttributeClassId} not loaded for component " f"<{component_gt.ComponentId}/<{component_gt.DisplayName}>\n" ) @@ -137,9 +138,9 @@ def load_components( component_decoder = default_component_decoder components = {} for type_name in [ + "Ads111xBasedComponents", "ResistiveHeaterComponents", "ElectricMeterComponents", - "MultipurposeSensorComponents", "OtherComponents", ]: for component_dict in layout.get(type_name, ()): @@ -199,7 +200,7 @@ def make_channel(cls, dc_dict: dict, nodes: dict[str, ShNode]) -> DataChannel: captured_by_node = nodes.get(dc_dict.get("CapturedByNodeName")) if about_node is None or captured_by_node is None: raise ValueError( - f"ERROR. DataChannel related nodes must exist!\n" + f"ERROR. DataChannel related nodes must exist for {dc_dict.get('Name')}!\n" f" For AboutNodeName <{dc_dict.get('AboutNodeName')}> " f"got {about_node}\n" f" for CapturedByNodeName <{dc_dict.get('CapturedByNodeName')}>" @@ -209,6 +210,71 @@ def make_channel(cls, dc_dict: dict, nodes: dict[str, ShNode]) -> DataChannel: about_node=about_node, captured_by_node=captured_by_node, **dc_dict ) + @classmethod + def check_dc_id_uniqueness( + cls, + data_channels: dict[str, DataChannel], + ) -> None: + id_counter = Counter(dc.Id for dc in data_channels.values()) + dupes = [node_id for node_id, count in id_counter.items() if count > 1] + if dupes: + raise DcError(f"Duplicate dc.Id(s) found: {dupes}") + + @classmethod + def check_data_channel_consistency( + cls, + components: dict[str, Component], + data_channels: dict[str, DataChannel], + ) -> None: + cls.check_dc_id_uniqueness(data_channels) + dc_names_by_component = set() + for c in components.values(): + channel_names = {config.ChannelName for config in c.gt.ConfigList} + if dc_names_by_component & channel_names: + raise DcError( + f"Channel name overlap!: {dc_names_by_component & channel_names}" + ) + dc_names_by_component.update(channel_names) + actual_dc_names = {dc.Name for dc in data_channels.values()} + if dc_names_by_component != actual_dc_names: + by_comp = list(dc_names_by_component) + by_comp.sort() + actual = list(actual_dc_names) + actual.sort() + raise DcError( + "Channel inconsistency! \n" + f"From Components:{by_comp}\n" + f"From DataChannel list:{actual}\n" + ) + + @classmethod + def check_node_consistency(cls, nodes: dict[str, ShNode]) -> None: + id_counter = Counter(node.ShNodeId for node in nodes.values()) + dupes = [node_id for node_id, count in id_counter.items() if count > 1] + if dupes: + raise DcError(f"Duplicate ShNodeId(s) found: {dupes}") + + @classmethod + def check_transactive_metering_consistency( + cls, nodes: dict[str, ShNode], data_channels: dict[str, DataChannel] + ) -> None: + transactive_nodes = {node for node in nodes.values() if node.InPowerMetering} + transactive_channels = { + dc for dc in data_channels.values() if dc.InPowerMetering + } + # Part 1: If a data channel is in transactive_channels, its about_node must be in transactive_nodes + for tc in transactive_channels: + if tc.about_node not in transactive_nodes: + raise DcError( + f"Data channel {tc} has about_node {tc.about_node}, which does not have InPowerMetering!" + ) + # Check condition 2: If a node is in transactive_nodes, there must be a data channel with that node as about_node + for node in transactive_nodes: + if not any(tc for tc in transactive_channels if tc.about_node == node): + raise DcError( + f"Node {node} is in transactive_nodes but no data channel with InPowerMetering has this node as about_node" + ) + @classmethod def load_data_channels( cls, @@ -248,7 +314,7 @@ def resolve_links( if node.component_id is not None: component = components.get(node.component_id, None) if component is None: - raise DataClassLoadingError( # noqa: TRY301 + raise DcError( # noqa: TRY301 f"{node.alias} component {node.component_id} not loaded!" ) if isinstance(component, ComponentResolver): @@ -346,16 +412,17 @@ def load_dict( # noqa: PLR0913 errors=errors, included_node_names=included_node_names, ) + data_channels = cls.load_data_channels( + layout=layout, + nodes=nodes, + raise_errors=raise_errors, + errors=errors, + ) load_args = { "cacs": cacs, "components": components, "nodes": nodes, - "data_channels": cls.load_data_channels( - layout=layout, - nodes=nodes, - raise_errors=raise_errors, - errors=errors, - ), + "data_channels": data_channels, } cls.resolve_links( load_args["nodes"], @@ -363,6 +430,25 @@ def load_dict( # noqa: PLR0913 raise_errors=raise_errors, errors=errors, ) + try: + cls.check_node_consistency(nodes) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("hardware.layout", nodes, e)) + try: + cls.check_data_channel_consistency( + components, + data_channels, + ) + cls.check_transactive_metering_consistency( + nodes, + data_channels, + ) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("data.channel.gt", layout, e)) return HardwareLayout(layout, **load_args) def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 @@ -420,7 +506,7 @@ def parent_node(self, alias: str) -> Optional[ShNode]: if not parent_alias: return None if parent_alias not in self.nodes: - raise DataClassLoadingError(f"{alias} is missing parent {parent_alias}!") + raise DcError(f"{alias} is missing parent {parent_alias}!") return self.node(parent_alias) def descendants(self, alias: str) -> List[ShNode]: diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index 9262d101..d639aa0f 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -2,7 +2,8 @@ from typing import Literal, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field, StrictInt, model_validator +from typing_extensions import Self from gwproto.enums import ActorClass, Role from gwproto.property_format import SpaceheatName, UUID4Str @@ -20,5 +21,21 @@ class SpaceheatNodeGt(BaseModel): title="InPowerMetering", default=None, ) + NameplatePowerW: Optional[StrictInt] = None TypeName: Literal["spaceheat.node.gt"] = "spaceheat.node.gt" - Version: Literal["110"] = "110" + Version: Literal["120"] = "120" + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: InPowerMetering requirements. + If InPowerMetering exists and is true, then NameplatePowerW must exist + """ + if self.InPowerMetering and self.NameplatePowerW is None: + raise ValueError( + "Axiom 1 failed! " + "If InPowerMetering exists and is true, then NameplatePowerW must exist" + ) + return self + + model_config = ConfigDict(extra="allow", use_enum_values=True) From a612923231adbef422bbdb08ef9c249628701c11 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 10:15:58 -0400 Subject: [PATCH 135/168] pre-commit fixes --- src/gwproto/types/ads111x_based_cac_gt.py | 2 -- tests/types/test_electric_meter_component_gt.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gwproto/types/ads111x_based_cac_gt.py b/src/gwproto/types/ads111x_based_cac_gt.py index a263b4eb..0fd93abf 100644 --- a/src/gwproto/types/ads111x_based_cac_gt.py +++ b/src/gwproto/types/ads111x_based_cac_gt.py @@ -46,5 +46,3 @@ def check_axiom_1(self) -> Self: """ # Implement check for axiom 1" return self - - diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 44bc9b60..9da2dc95 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -6,7 +6,7 @@ def test_electric_meter_component_gt_generated() -> None: - d = d = { + d = { "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", "ComponentId": "dce5c63d-8a38-439e-88ac-dd5ad845e9ca", "ConfigList": [ From 9867970fd33e2be352dac46e3b8b1da8bb4caddf Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 23 Sep 2024 12:43:35 -0400 Subject: [PATCH 136/168] add sample thermostat channels and nodes --- tests/config/hardware-layout.json | 96 ++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index bd9d4cff..dca732b8 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -396,7 +396,48 @@ "HwUid": "bbbb2222" } ], - "OtherComponents": [], + "OtherComponents": [ + { + "ComponentId": "2c302eed-2f86-4ed6-8019-df31c22e1704", + "DisplayName": "Downstairs T6 Thermostat", + "ComponentAttributeClassId": "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", + "ConfigList": [ + { + "AsyncCapture": false, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-temp", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "Fahrenheit", + "Version": "000" + }, + { + "AsyncCapture": false, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-set", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "Fahrenheit", + "Version": "000" + }, + { + "AsyncCapture": true, + "AsyncCaptureDelta": 1, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-state", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "Fahrenheit", + "Version": "000" + } + ], + "TypeName": "component.gt", + "Version": "001" + } + ], "ResistiveHeaterCacs": [ { "ComponentAttributeClassId": "cf1f2587-7462-4701-b962-d2b264744c1d", @@ -613,6 +654,15 @@ "TypeName": "spaceheat.node.gt", "Version": "120" }, + { + "Alias": "zone1-down", + "Role": "Unknown", + "ActorClass": "NoActor", + "DisplayName": "Down Zone", + "ShNodeId": "be8df9e8-1a72-4574-9575-dca70dbec214", + "TypeName": "spaceheat.node.gt", + "Version": "120" + }, { "Alias": "analog-temp", "Role": "Unknown", @@ -621,6 +671,16 @@ "ShNodeId": "8bfc40f1-7c84-4e88-9214-78c304730ae3", "TypeName": "spaceheat.node.gt", "Version": "120" + }, + { + "Alias": "zone1-down-stat", + "Role": "Unknown", + "ActorClass": "HoneywellThermostat", + "DisplayName": "Zone 1 (Down) Thermostat", + "ComponentId": "2c302eed-2f86-4ed6-8019-df31c22e1704", + "ShNodeId": "8c9c155c-5b9a-448c-8df3-2edd9f4ebdb7", + "TypeName": "spaceheat.node.gt", + "Version": "120" } ], "DataChannels": [ @@ -774,6 +834,40 @@ "Id": "7876ebb2-03d2-4c7f-9046-011b7c5d4deb", "TypeName": "data.channel.gt", "Version": "001" + }, + { + "Name": "zone1-down-temp", + "DisplayName": "Zone 1 Down Temp", + "AboutNodeName": "zone1-down", + "CapturedByNodeName": "zone1-down-stat", + "TelemetryName": "AirTempFTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "b90caf01-0441-40d9-9e1b-7162ebda5aa7", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "zone1-down-set", + "DisplayName": "Zone 1 Down Setpoint", + "AboutNodeName": "zone1-down-stat", + "CapturedByNodeName": "zone1-down-stat", + "TelemetryName": "AirTempFTimes1000", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "5b412e33-fbba-4237-9da9-993ce7880518", + "TypeName": "data.channel.gt", + "Version": "001" + }, + { + "Name": "zone1-down-state", + "DisplayName": "Zone 1 Down Setpoint", + "AboutNodeName": "zone1-down-stat", + "CapturedByNodeName": "zone1-down-stat", + "TelemetryName": "ThermostatState", + "TerminalAssetAlias": "d1.isone.ct.newhaven.orange1.ta", + "Id": "ba246554-6c17-424a-bfef-8bb80d4c4a6d", + "TypeName": "data.channel.gt", + "Version": "001" } + ] } From a22e8c90d7fb6dd26cd11e81bfb939c3d6558118 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 24 Sep 2024 12:52:42 -0400 Subject: [PATCH 137/168] move terminal block consistency check out of scada and into layotu --- src/gwproto/data_classes/hardware_layout.py | 21 +++++++++++++++++++++ tests/config/hardware-layout.json | 3 ++- tests/data_classes/test_hardware_layout.py | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 0b101085..67bb5e2b 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -39,6 +39,8 @@ ElectricMeterCacGt, ) +from gwproto.data_classes.components import Ads111xBasedComponent + T = TypeVar("T") @@ -274,6 +276,16 @@ def check_transactive_metering_consistency( raise DcError( f"Node {node} is in transactive_nodes but no data channel with InPowerMetering has this node as about_node" ) + + @classmethod + def check_ads_terminal_block_consistency(cls, c: Ads111xBasedComponent) -> None: + possible_indices = set(range(1, c.cac.TotalTerminalBlocks+1)) # e,g {1, .., 12} + actual_indices = {tc.TerminalBlockIdx for tc in c.gt.ThermistorConfigList} + if not actual_indices.issubset(possible_indices): + raise DcError( + f"Terminal Block indices {actual_indices}" + f"When Ads only has {c.cac.TotalTerminalBlocks} terminal blocks!" + ) @classmethod def load_data_channels( @@ -449,6 +461,15 @@ def load_dict( # noqa: PLR0913 if raise_errors: raise errors.append(LoadError("data.channel.gt", layout, e)) + ads111x_components = [comp for comp in components.values() if isinstance(comp, Ads111xBasedComponent)] + for c in ads111x_components: + + try: + cls.check_ads_terminal_block_consistency(c) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("ads111x.based.component.gt", c.gt.model_dump(), e)) return HardwareLayout(layout, **load_args) def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index dca732b8..a8c73d8b 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -150,7 +150,7 @@ { "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", "MinPollPeriodMs": 200, - "MakeModel": "GRIDWORKS__MULTITEMP1", + "MakeModel": "GRIDWORKS__TSNAP1", "AdsI2cAddressList": ["0x4b", "0x48", "0x49"], "TotalTerminalBlocks": 12, "TelemetryNameList": ["WaterTempCTimes1000", "AirTempCTimes1000"], @@ -669,6 +669,7 @@ "ActorClass": "MultipurposeSensor", "DisplayName": "GridWorks MultiTemp", "ShNodeId": "8bfc40f1-7c84-4e88-9214-78c304730ae3", + "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", "TypeName": "spaceheat.node.gt", "Version": "120" }, diff --git a/tests/data_classes/test_hardware_layout.py b/tests/data_classes/test_hardware_layout.py index d4924196..2a26c009 100644 --- a/tests/data_classes/test_hardware_layout.py +++ b/tests/data_classes/test_hardware_layout.py @@ -1,5 +1,19 @@ from gwproto import HardwareLayout +import pytest +import json +from pathlib import Path +from gw.errors import DcError def test_hardware_layout() -> None: HardwareLayout.load("tests/config/hardware-layout.json") + + # test Ads111x Terminal Block consistency + with Path("tests/config/hardware-layout.json").open() as f: + layout_dict = json.loads(f.read()) + + # Ads111xBasedCacs have at most 12 terminal blocks. Make an impossible one + layout_dict["Ads111xBasedComponents"][0]["ThermistorConfigList"][0]["TerminalBlockIdx"] = 13 + + with pytest.raises(DcError): + HardwareLayout.load_dict(layout_dict) From 6e8b3ec98e0d88fcb9d7d3f57e4116c1fc603408 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 24 Sep 2024 13:17:47 -0400 Subject: [PATCH 138/168] ThermistorDataProcessingConfig inherits ChannelConfig --- src/gwproto/data_classes/hardware_layout.py | 21 +- .../thermistor_data_processing_config.py | 5 +- tests/config/hardware-layout.json | 54 ++ tests/data_classes/test_hardware_layout.py | 12 +- .../types/test_ads111x_based_component_gt.py | 498 +++++++++--------- .../test_thermistor_data_processing_config.py | 6 + 6 files changed, 330 insertions(+), 266 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 67bb5e2b..f3a2563a 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -17,7 +17,7 @@ from gw.errors import DcError import gwproto.data_classes.components -from gwproto.data_classes.components import Component +from gwproto.data_classes.components import Ads111xBasedComponent, Component from gwproto.data_classes.components.component import ComponentOnly from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, @@ -39,8 +39,6 @@ ElectricMeterCacGt, ) -from gwproto.data_classes.components import Ads111xBasedComponent - T = TypeVar("T") @@ -276,10 +274,12 @@ def check_transactive_metering_consistency( raise DcError( f"Node {node} is in transactive_nodes but no data channel with InPowerMetering has this node as about_node" ) - + @classmethod def check_ads_terminal_block_consistency(cls, c: Ads111xBasedComponent) -> None: - possible_indices = set(range(1, c.cac.TotalTerminalBlocks+1)) # e,g {1, .., 12} + possible_indices = set( + range(1, c.cac.TotalTerminalBlocks + 1) + ) # e,g {1, .., 12} actual_indices = {tc.TerminalBlockIdx for tc in c.gt.ThermistorConfigList} if not actual_indices.issubset(possible_indices): raise DcError( @@ -461,15 +461,20 @@ def load_dict( # noqa: PLR0913 if raise_errors: raise errors.append(LoadError("data.channel.gt", layout, e)) - ads111x_components = [comp for comp in components.values() if isinstance(comp, Ads111xBasedComponent)] + ads111x_components = [ + comp + for comp in components.values() + if isinstance(comp, Ads111xBasedComponent) + ] for c in ads111x_components: - try: cls.check_ads_terminal_block_consistency(c) except Exception as e: # noqa: PERF203 if raise_errors: raise - errors.append(LoadError("ads111x.based.component.gt", c.gt.model_dump(), e)) + errors.append( + LoadError("ads111x.based.component.gt", c.gt.model_dump(), e) + ) return HardwareLayout(layout, **load_args) def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 diff --git a/src/gwproto/types/thermistor_data_processing_config.py b/src/gwproto/types/thermistor_data_processing_config.py index 137752bc..afc35ff4 100644 --- a/src/gwproto/types/thermistor_data_processing_config.py +++ b/src/gwproto/types/thermistor_data_processing_config.py @@ -2,15 +2,16 @@ from typing import Literal, Optional -from pydantic import BaseModel, ConfigDict, PositiveInt +from pydantic import ConfigDict, PositiveInt from gwproto.enums import MakeModel, ThermistorDataMethod from gwproto.property_format import ( SpaceheatName, ) +from gwproto.types.channel_config import ChannelConfig -class ThermistorDataProcessingConfig(BaseModel): +class ThermistorDataProcessingConfig(ChannelConfig): ChannelName: SpaceheatName TerminalBlockIdx: PositiveInt ThermistorMakeModel: MakeModel diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index a8c73d8b..8844f9d6 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -273,6 +273,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 1, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -282,6 +288,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 2, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -291,6 +303,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 3, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -300,6 +318,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 4, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -309,6 +333,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 5, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -318,6 +348,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 6, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -327,6 +363,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 7, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -336,6 +378,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 8, "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": true, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" }, @@ -345,6 +393,12 @@ "DataProcessingMethod": "SimpleBeta", "TerminalBlockIdx": 9, "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "AsyncCapture": true, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000" } diff --git a/tests/data_classes/test_hardware_layout.py b/tests/data_classes/test_hardware_layout.py index 2a26c009..86879508 100644 --- a/tests/data_classes/test_hardware_layout.py +++ b/tests/data_classes/test_hardware_layout.py @@ -1,19 +1,23 @@ -from gwproto import HardwareLayout -import pytest import json from pathlib import Path +import pytest from gw.errors import DcError +from gwproto import HardwareLayout + + def test_hardware_layout() -> None: HardwareLayout.load("tests/config/hardware-layout.json") # test Ads111x Terminal Block consistency with Path("tests/config/hardware-layout.json").open() as f: layout_dict = json.loads(f.read()) - + # Ads111xBasedCacs have at most 12 terminal blocks. Make an impossible one - layout_dict["Ads111xBasedComponents"][0]["ThermistorConfigList"][0]["TerminalBlockIdx"] = 13 + layout_dict["Ads111xBasedComponents"][0]["ThermistorConfigList"][0][ + "TerminalBlockIdx" + ] = 13 with pytest.raises(DcError): HardwareLayout.load_dict(layout_dict) diff --git a/tests/types/test_ads111x_based_component_gt.py b/tests/types/test_ads111x_based_component_gt.py index 620f03d8..dd2cedbb 100644 --- a/tests/types/test_ads111x_based_component_gt.py +++ b/tests/types/test_ads111x_based_component_gt.py @@ -4,258 +4,252 @@ def test_ads111x_based_component_gt_generated() -> None: - d = { - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", - "ConfigList": [ - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-swt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-rwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-lwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-ewt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 500, - "CapturePeriodS": 60, - "ChannelName": "oat", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": False, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 5, - "ChannelName": "zone2-up-gw-temp", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": False, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 5, - "ChannelName": "zone1-down-gw-temp", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-well", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000", - }, - ], - "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", - "OpenVoltageByAds": [4.95, 4.95, 4.95], - "ThermistorConfigList": [ - { - "ChannelName": "dist-swt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 1, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "dist-rwt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 2, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "hp-lwt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 3, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "hp-ewt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 4, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "store-hot-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 5, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "store-cold-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 6, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "buffer-hot-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 7, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "buffer-cold-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 8, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "oat", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 9, - "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "zone2-up-gw-temp", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 10, - "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "zone1-down-gw-temp", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 11, - "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - { - "ChannelName": "buffer-well", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 12, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "TypeName": "thermistor.data.processing.config", - "Version": "000", - }, - ], - "TypeName": "ads111x.based.component.gt", - "Version": "000", - } + d = { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", + "ConfigList": [ + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-swt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "dist-rwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-lwt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "hp-ewt", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "store-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-hot-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "ChannelName": "buffer-cold-pipe", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + }, + { + "AsyncCapture": True, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "ChannelName": "oat", + "Exponent": 3, + "PollPeriodMs": 200, + "TypeName": "channel.config", + "Unit": "Celcius", + "Version": "000" + } + ], + "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", + "OpenVoltageByAds": [4.95, 4.95, 4.95], + "ThermistorConfigList": [ + { + "ChannelName": "dist-swt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 1, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "dist-rwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 2, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "hp-lwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 3, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "hp-ewt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 4, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "store-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 5, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "store-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 6, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "buffer-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 7, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "buffer-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 8, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + }, + { + "ChannelName": "oat", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 9, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "AsyncCapture": True, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "thermistor.data.processing.config", + "Version": "000" + } + ], + "TypeName": "ads111x.based.component.gt", + "Version": "000" + } d2 = Ads111xBasedComponentGt.model_validate(d).model_dump(exclude_none=True) diff --git a/tests/types/test_thermistor_data_processing_config.py b/tests/types/test_thermistor_data_processing_config.py index 9fc40b1f..ef5d8993 100644 --- a/tests/types/test_thermistor_data_processing_config.py +++ b/tests/types/test_thermistor_data_processing_config.py @@ -11,6 +11,12 @@ def test_thermistor_data_processing_config_generated() -> None: "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", "DataProcessingMethod": "SimpleBeta", "DataProcessingDescription": "using a beta of 3977.", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", "TypeName": "thermistor.data.processing.config", "Version": "000", } From 02139228255bb4e6f5325a0b1f804ac1b0de4250 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 24 Sep 2024 16:37:16 -0400 Subject: [PATCH 139/168] AdsComponent ConfigList is list of AdsChannelConfig objects --- src/gwproto/data_classes/hardware_layout.py | 2 +- src/gwproto/types/__init__.py | 8 +- .../types/ads111x_based_component_gt.py | 30 +- ...essing_config.py => ads_channel_config.py} | 8 +- src/gwproto/types/component_gt.py | 15 +- tests/config/hardware-layout.json | 121 +----- tests/data_classes/test_hardware_layout.py | 2 +- .../types/test_ads111x_based_component_gt.py | 394 +++++++----------- ...g_config.py => test_ads_channel_config.py} | 14 +- 9 files changed, 198 insertions(+), 396 deletions(-) rename src/gwproto/types/{thermistor_data_processing_config.py => ads_channel_config.py} (72%) rename tests/types/{test_thermistor_data_processing_config.py => test_ads_channel_config.py} (65%) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index f3a2563a..fbf5d870 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -280,7 +280,7 @@ def check_ads_terminal_block_consistency(cls, c: Ads111xBasedComponent) -> None: possible_indices = set( range(1, c.cac.TotalTerminalBlocks + 1) ) # e,g {1, .., 12} - actual_indices = {tc.TerminalBlockIdx for tc in c.gt.ThermistorConfigList} + actual_indices = {tc.TerminalBlockIdx for tc in c.gt.ConfigList} if not actual_indices.issubset(possible_indices): raise DcError( f"Terminal Block indices {actual_indices}" diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index dc3d0a17..66ee421a 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -2,6 +2,9 @@ from gwproto.types.ads111x_based_cac_gt import Ads111xBasedCacGt from gwproto.types.ads111x_based_component_gt import Ads111xBasedComponentGt +from gwproto.types.ads_channel_config import ( + AdsChannelConfig, +) from gwproto.types.channel_config import ChannelConfig from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt @@ -54,9 +57,6 @@ from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, ) -from gwproto.types.thermistor_data_processing_config import ( - ThermistorDataProcessingConfig, -) __all__ = [ "Ads111xBasedCacGt", @@ -87,7 +87,7 @@ "SnapshotSpaceheat", "SpaceheatNodeGt", "TelemetrySnapshotSpaceheat", - "ThermistorDataProcessingConfig", + "AdsChannelConfig", "cacs", # noqa: F822 "components", # noqa: F822 ] diff --git a/src/gwproto/types/ads111x_based_component_gt.py b/src/gwproto/types/ads111x_based_component_gt.py index f40d76f5..7cf1e997 100644 --- a/src/gwproto/types/ads111x_based_component_gt.py +++ b/src/gwproto/types/ads111x_based_component_gt.py @@ -8,15 +8,15 @@ from gwproto.property_format import ( check_is_near5, ) -from gwproto.types.component_gt import ComponentGt -from gwproto.types.thermistor_data_processing_config import ( - ThermistorDataProcessingConfig, +from gwproto.types.ads_channel_config import ( + AdsChannelConfig, ) +from gwproto.types.component_gt import ComponentGt class Ads111xBasedComponentGt(ComponentGt): OpenVoltageByAds: List[float] - ThermistorConfigList: List[ThermistorDataProcessingConfig] + ConfigList: List[AdsChannelConfig] TypeName: Literal["ads111x.based.component.gt"] = "ads111x.based.component.gt" Version: Literal["000"] = "000" @@ -34,25 +34,15 @@ def _check_open_voltage_by_ads(cls, v: List[float]) -> List[float]: ) from e return v - @field_validator("ThermistorConfigList") + @field_validator("ConfigList") @classmethod - def check_thermistor_config_list( - cls, v: List[ThermistorDataProcessingConfig] - ) -> List[ThermistorDataProcessingConfig]: + def check_config_list( + cls, v: List[AdsChannelConfig] + ) -> List[AdsChannelConfig]: """ - Axiom 1: Terminal Block consistency and Channel Name uniqueness.. + Axiom 1: Terminal Block consistency and Channel Name uniqueness. Terminal Block consistency and Channel Name uniqueness. - Each TerminalBlockIdx occurs at - most once in the ThermistorConfigList - Each data channel occurs at most once in the ThermistorConfigList + most once in the ConfigList .Each data channel occurs at most once in the ConfigList """ # Implement Axiom(s) return v - - @model_validator(mode="after") - def check_axiom_2(self) -> Self: - """ - Axiom 2: ThermistorConfig, ChannelConfig consistency. - set(map(lambda x: x.ChannelName, ThermistorConfigList)) is equal to - set(map(lambda x: x.ChannelName, ConfigList)) - """ - # Implement check for axiom 2" - return self diff --git a/src/gwproto/types/thermistor_data_processing_config.py b/src/gwproto/types/ads_channel_config.py similarity index 72% rename from src/gwproto/types/thermistor_data_processing_config.py rename to src/gwproto/types/ads_channel_config.py index afc35ff4..13a6f3cd 100644 --- a/src/gwproto/types/thermistor_data_processing_config.py +++ b/src/gwproto/types/ads_channel_config.py @@ -1,4 +1,4 @@ -"""Type thermistor.data.processing.config, version 000""" +"""Type ads.channel.config, version 000""" from typing import Literal, Optional @@ -11,14 +11,14 @@ from gwproto.types.channel_config import ChannelConfig -class ThermistorDataProcessingConfig(ChannelConfig): +class AdsChannelConfig(ChannelConfig): ChannelName: SpaceheatName TerminalBlockIdx: PositiveInt ThermistorMakeModel: MakeModel DataProcessingMethod: Optional[ThermistorDataMethod] = None DataProcessingDescription: Optional[str] = None - TypeName: Literal["thermistor.data.processing.config"] = ( - "thermistor.data.processing.config" + TypeName: Literal["ads.channel.config"] = ( + "ads.channel.config" ) Version: Literal["000"] = "000" diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index a84c06eb..ba0c22d3 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -2,7 +2,7 @@ from typing import List, Literal, Optional -from pydantic import BaseModel +from pydantic import BaseModel, field_validator from gwproto.property_format import UUID4Str from gwproto.types.channel_config import ChannelConfig @@ -16,3 +16,16 @@ class ComponentGt(BaseModel): HwUid: Optional[str] = None TypeName: Literal["component.gt"] = "component.gt" Version: Literal["001"] = "001" + + + @field_validator("ConfigList") + @classmethod + def check_config_list( + cls, v: List[ChannelConfig] + ) -> List[ChannelConfig]: + """ + Axiom 1: Channel Name uniqueness. Data Channel names are + unique in the config list + """ + # Implement Axiom(s) + return v diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 8844f9d6..6ba5bd31 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -163,110 +163,9 @@ { "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", - "ConfigList": [ - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-swt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-rwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-lwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-ewt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": true, - "AsyncCaptureDelta": 500, - "CapturePeriodS": 60, - "ChannelName": "oat", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - } - ], "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", "OpenVoltageByAds": [4.95, 4.95, 4.95], - "ThermistorConfigList": [ + "ConfigList": [ { "ChannelName": "dist-swt", "DataProcessingDescription": "Using beta of 3977", @@ -279,7 +178,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -294,7 +193,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -309,7 +208,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -324,7 +223,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -339,7 +238,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -354,7 +253,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -369,7 +268,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -384,7 +283,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" }, { @@ -399,7 +298,7 @@ "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000" } ], diff --git a/tests/data_classes/test_hardware_layout.py b/tests/data_classes/test_hardware_layout.py index 86879508..aa12dad6 100644 --- a/tests/data_classes/test_hardware_layout.py +++ b/tests/data_classes/test_hardware_layout.py @@ -15,7 +15,7 @@ def test_hardware_layout() -> None: layout_dict = json.loads(f.read()) # Ads111xBasedCacs have at most 12 terminal blocks. Make an impossible one - layout_dict["Ads111xBasedComponents"][0]["ThermistorConfigList"][0][ + layout_dict["Ads111xBasedComponents"][0]["ConfigList"][0][ "TerminalBlockIdx" ] = 13 diff --git a/tests/types/test_ads111x_based_component_gt.py b/tests/types/test_ads111x_based_component_gt.py index dd2cedbb..2545e9e4 100644 --- a/tests/types/test_ads111x_based_component_gt.py +++ b/tests/types/test_ads111x_based_component_gt.py @@ -4,254 +4,154 @@ def test_ads111x_based_component_gt_generated() -> None: - d = { - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", - "ConfigList": [ - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-swt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "dist-rwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-lwt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "hp-ewt", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "store-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-hot-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "ChannelName": "buffer-cold-pipe", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 500, - "CapturePeriodS": 60, - "ChannelName": "oat", - "Exponent": 3, - "PollPeriodMs": 200, - "TypeName": "channel.config", - "Unit": "Celcius", - "Version": "000" - } - ], - "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", - "OpenVoltageByAds": [4.95, 4.95, 4.95], - "ThermistorConfigList": [ - { - "ChannelName": "dist-swt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 1, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "dist-rwt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 2, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "hp-lwt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 3, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "hp-ewt", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 4, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "store-hot-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 5, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "store-cold-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 6, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "buffer-hot-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 7, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "buffer-cold-pipe", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 8, - "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", - "AsyncCapture": True, - "AsyncCaptureDelta": 250, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - }, - { - "ChannelName": "oat", - "DataProcessingDescription": "Using beta of 3977", - "DataProcessingMethod": "SimpleBeta", - "TerminalBlockIdx": 9, - "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", - "AsyncCapture": True, - "AsyncCaptureDelta": 500, - "CapturePeriodS": 60, - "Exponent": 3, - "PollPeriodMs": 200, - "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", - "Version": "000" - } - ], - "TypeName": "ads111x.based.component.gt", - "Version": "000" - } + d = { + "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", + "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", + + "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", + "OpenVoltageByAds": [4.95, 4.95, 4.95], + "ConfigList": [ + { + "ChannelName": "dist-swt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 1, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "dist-rwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 2, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "hp-lwt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 3, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "hp-ewt", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 4, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "store-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 5, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "store-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 6, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "buffer-hot-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 7, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "buffer-cold-pipe", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 8, + "ThermistorMakeModel": "TEWA__TT0P10KC3T1051500", + "AsyncCapture": True, + "AsyncCaptureDelta": 250, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + { + "ChannelName": "oat", + "DataProcessingDescription": "Using beta of 3977", + "DataProcessingMethod": "SimpleBeta", + "TerminalBlockIdx": 9, + "ThermistorMakeModel": "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN", + "AsyncCapture": True, + "AsyncCaptureDelta": 500, + "CapturePeriodS": 60, + "Exponent": 3, + "PollPeriodMs": 200, + "Unit": "Celcius", + "TypeName": "ads.channel.config", + "Version": "000", + }, + ], + "TypeName": "ads111x.based.component.gt", + "Version": "000", + } d2 = Ads111xBasedComponentGt.model_validate(d).model_dump(exclude_none=True) - assert type(d2["ThermistorConfigList"][0]["DataProcessingMethod"]) is str + assert type(d2["ConfigList"][0]["DataProcessingMethod"]) is str assert d2 == d diff --git a/tests/types/test_thermistor_data_processing_config.py b/tests/types/test_ads_channel_config.py similarity index 65% rename from tests/types/test_thermistor_data_processing_config.py rename to tests/types/test_ads_channel_config.py index ef5d8993..04918312 100644 --- a/tests/types/test_thermistor_data_processing_config.py +++ b/tests/types/test_ads_channel_config.py @@ -1,10 +1,10 @@ -"""Tests thermistor.data.processing.config type, version 000""" +"""Tests ads.channel.config type, version 000""" from gwproto.enums import MakeModel, ThermistorDataMethod -from gwproto.types import ThermistorDataProcessingConfig +from gwproto.types import AdsChannelConfig -def test_thermistor_data_processing_config_generated() -> None: +def test_ads_channel_config_generated() -> None: d = { "ChannelName": "hp-ewt", "TerminalBlockIdx": 4, @@ -17,11 +17,11 @@ def test_thermistor_data_processing_config_generated() -> None: "Exponent": 3, "PollPeriodMs": 200, "Unit": "Celcius", - "TypeName": "thermistor.data.processing.config", + "TypeName": "ads.channel.config", "Version": "000", } - d2 = ThermistorDataProcessingConfig.model_validate(d).model_dump(exclude_none=True) + d2 = AdsChannelConfig.model_validate(d).model_dump(exclude_none=True) assert d2 == d @@ -33,13 +33,13 @@ def test_thermistor_data_processing_config_generated() -> None: d2 = dict(d, ThermistorMakeModel="unknown_enum_thing") assert ( - ThermistorDataProcessingConfig(**d2).ThermistorMakeModel == MakeModel.default() + AdsChannelConfig(**d2).ThermistorMakeModel == MakeModel.default() ) assert type(d2["DataProcessingMethod"]) is str d2 = dict(d, DataProcessingMethod="unknown_enum_thing") assert ( - ThermistorDataProcessingConfig(**d2).DataProcessingMethod + AdsChannelConfig(**d2).DataProcessingMethod == ThermistorDataMethod.default() ) From 96056c708c516c7d17b8821c51e1ab9ca8107edc Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 24 Sep 2024 20:07:17 -0400 Subject: [PATCH 140/168] ElectricMeterComponent ConfigList is list of ElectricMeterChannelConfig objects --- src/gwproto/types/__init__.py | 4 +- .../types/ads111x_based_component_gt.py | 7 +- src/gwproto/types/ads_channel_config.py | 4 +- src/gwproto/types/component_gt.py | 9 +- src/gwproto/types/egauge_io.py | 19 --- .../types/electric_meter_channel_config.py | 14 ++ .../types/electric_meter_component_gt.py | 40 +++--- tests/config/hardware-layout.json | 100 ++++++------- .../types/test_ads111x_based_component_gt.py | 1 - tests/types/test_ads_channel_config.py | 9 +- tests/types/test_egauge_io.py | 22 --- .../test_electric_meter_channel_config.py | 28 ++++ .../types/test_electric_meter_component_gt.py | 132 +++++++----------- 13 files changed, 163 insertions(+), 226 deletions(-) delete mode 100644 src/gwproto/types/egauge_io.py create mode 100644 src/gwproto/types/electric_meter_channel_config.py delete mode 100644 tests/types/test_egauge_io.py create mode 100644 tests/types/test_electric_meter_channel_config.py diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index 66ee421a..b722780b 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -9,13 +9,13 @@ from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt from gwproto.types.data_channel_gt import DataChannelGt -from gwproto.types.egauge_io import EgaugeIo from gwproto.types.egauge_register_config import ( EgaugeRegisterConfig, ) from gwproto.types.electric_meter_cac_gt import ( ElectricMeterCacGt, ) +from gwproto.types.electric_meter_channel_config import ElectricMeterChannelConfig from gwproto.types.electric_meter_component_gt import ElectricMeterComponentGt from gwproto.types.fibaro_smart_implant_component_gt import ( FibaroSmartImplantComponentGt, @@ -65,10 +65,10 @@ "ComponentAttributeClassGt", "ComponentGt", "DataChannelGt", - "EgaugeIo", "EgaugeRegisterConfig", "ElectricMeterCacGt", "ElectricMeterComponentGt", + "ElectricMeterChannelConfig", "FibaroSmartImplantComponentGt", "GtShBooleanactuatorCmdStatus", "GtShCliAtnCmd", diff --git a/src/gwproto/types/ads111x_based_component_gt.py b/src/gwproto/types/ads111x_based_component_gt.py index 7cf1e997..b53f4775 100644 --- a/src/gwproto/types/ads111x_based_component_gt.py +++ b/src/gwproto/types/ads111x_based_component_gt.py @@ -2,8 +2,7 @@ from typing import List, Literal -from pydantic import ConfigDict, field_validator, model_validator -from typing_extensions import Self +from pydantic import ConfigDict, field_validator from gwproto.property_format import ( check_is_near5, @@ -36,9 +35,7 @@ def _check_open_voltage_by_ads(cls, v: List[float]) -> List[float]: @field_validator("ConfigList") @classmethod - def check_config_list( - cls, v: List[AdsChannelConfig] - ) -> List[AdsChannelConfig]: + def check_config_list(cls, v: List[AdsChannelConfig]) -> List[AdsChannelConfig]: """ Axiom 1: Terminal Block consistency and Channel Name uniqueness. Terminal Block consistency and Channel Name uniqueness. - Each TerminalBlockIdx occurs at diff --git a/src/gwproto/types/ads_channel_config.py b/src/gwproto/types/ads_channel_config.py index 13a6f3cd..929c470a 100644 --- a/src/gwproto/types/ads_channel_config.py +++ b/src/gwproto/types/ads_channel_config.py @@ -17,9 +17,7 @@ class AdsChannelConfig(ChannelConfig): ThermistorMakeModel: MakeModel DataProcessingMethod: Optional[ThermistorDataMethod] = None DataProcessingDescription: Optional[str] = None - TypeName: Literal["ads.channel.config"] = ( - "ads.channel.config" - ) + TypeName: Literal["ads.channel.config"] = "ads.channel.config" Version: Literal["000"] = "000" model_config = ConfigDict(extra="allow", use_enum_values=True) diff --git a/src/gwproto/types/component_gt.py b/src/gwproto/types/component_gt.py index ba0c22d3..4147fcc0 100644 --- a/src/gwproto/types/component_gt.py +++ b/src/gwproto/types/component_gt.py @@ -17,15 +17,12 @@ class ComponentGt(BaseModel): TypeName: Literal["component.gt"] = "component.gt" Version: Literal["001"] = "001" - @field_validator("ConfigList") @classmethod - def check_config_list( - cls, v: List[ChannelConfig] - ) -> List[ChannelConfig]: + def check_config_list(cls, v: List[ChannelConfig]) -> List[ChannelConfig]: """ - Axiom 1: Channel Name uniqueness. Data Channel names are - unique in the config list + Axiom 1: Channel Name uniqueness. Data Channel names are + unique in the config list """ # Implement Axiom(s) return v diff --git a/src/gwproto/types/egauge_io.py b/src/gwproto/types/egauge_io.py deleted file mode 100644 index 5c19620e..00000000 --- a/src/gwproto/types/egauge_io.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Type egauge.io, version 000""" - -from typing import Literal - -from pydantic import BaseModel - -from gwproto.property_format import ( - SpaceheatName, -) -from gwproto.types.egauge_register_config import ( - EgaugeRegisterConfig, -) - - -class EgaugeIo(BaseModel): - ChannelName: SpaceheatName - InputConfig: EgaugeRegisterConfig - TypeName: Literal["egauge.io"] = "egauge.io" - Version: Literal["001"] = "001" diff --git a/src/gwproto/types/electric_meter_channel_config.py b/src/gwproto/types/electric_meter_channel_config.py new file mode 100644 index 00000000..9903d130 --- /dev/null +++ b/src/gwproto/types/electric_meter_channel_config.py @@ -0,0 +1,14 @@ +"""Type electric.meter.channel.config, version 000""" + +from typing import Literal, Optional + +from gwproto.types.channel_config import ChannelConfig +from gwproto.types.egauge_register_config import ( + EgaugeRegisterConfig, +) + + +class ElectricMeterChannelConfig(ChannelConfig): + EgaugeRegisterConfig: Optional[EgaugeRegisterConfig] + TypeName: Literal["electric.meter.channel.config"] = "electric.meter.channel.config" + Version: Literal["000"] = "000" diff --git a/src/gwproto/types/electric_meter_component_gt.py b/src/gwproto/types/electric_meter_component_gt.py index ccc56f29..fb27f400 100644 --- a/src/gwproto/types/electric_meter_component_gt.py +++ b/src/gwproto/types/electric_meter_component_gt.py @@ -2,22 +2,37 @@ from typing import List, Literal, Optional, Self -from pydantic import PositiveInt, model_validator +from pydantic import PositiveInt, field_validator, model_validator from gwproto.types import ComponentGt -from gwproto.types.egauge_io import EgaugeIo +from gwproto.types.electric_meter_channel_config import ElectricMeterChannelConfig class ElectricMeterComponentGt(ComponentGt): ModbusHost: Optional[str] = None ModbusPort: Optional[PositiveInt] = None - EgaugeIoList: List[EgaugeIo] + ConfigList: List[ElectricMeterChannelConfig] TypeName: Literal["electric.meter.component.gt"] = "electric.meter.component.gt" + @field_validator("ConfigList") + @classmethod + def check_config_list( + cls, v: List[ElectricMeterChannelConfig] + ) -> List[ElectricMeterChannelConfig]: + """ + Axiom 1: Channel Name uniqueness. Data Channel names are + unique in the config list + + Axiom 2: Egauge Config consistency. If one of the ElectricMeterChannelConfigs + has an EgaugeRegisterConfig, then they all do. + """ + # Implement Axiom(s) + return v + @model_validator(mode="after") def check_axiom_1(self) -> Self: """ - Axiom 1: Modbus consistency. + Axiom 3: Modbus consistency. ModbusHost is None if and only if ModbusPort is None """ # Implement check for axiom 1" @@ -26,20 +41,9 @@ def check_axiom_1(self) -> Self: @model_validator(mode="after") def check_axiom_2(self) -> Self: """ - Axiom 2: Egauge4030 Means Modbus. - If the EgaugeIoList has non-zero length, then the ModbusHost is not None + Axiom 4: Egauge4030 Means Modbus. + If any of the ElectricMeterChannelConfigs have EgaugeRegisterConfig, then the ModbusHost + is not None """ # Implement check for axiom 2" return self - - @model_validator(mode="after") - def check_axiom_3(self) -> Self: - """ - Axiom 3: Channel Name Consistency. - If the EgaugeIoList has non-zero length: - 1) Len(EgaugeIoList) == Len(ConfigList) - 2) There are no duplicates of ChannelName in the ConfigList or EgaugeIoList - 3) The set of ChannelNames in IoConfig is equal to the set of ChannelNames in ConfigList - """ - # Implement check for axiom 3" - return self diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 6ba5bd31..03615e1e 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -39,7 +39,17 @@ "ChannelName": "hp-odu-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "TypeName": "channel.config", + "EgaugeRegisterConfig": { + "Address": 9000, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" }, @@ -50,7 +60,17 @@ "ChannelName": "hp-idu-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "TypeName": "channel.config", + "EgaugeRegisterConfig": { + "Address": 9002, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" }, @@ -61,7 +81,17 @@ "ChannelName": "store-pump-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "TypeName": "channel.config", + "EgaugeRegisterConfig": { + "Address": 9014, + "Denominator": 1, + "Description": "change in value", + "Name": "", + "Type": "f32", + "TypeName": "egauge.register.config", + "Unit": "W", + "Version": "000" + }, + "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" }, @@ -72,61 +102,7 @@ "ChannelName": "elt1-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000" - } - ], - "HwUid": "1001ab", - "EgaugeIoList": [ - { - "ChannelName": "hp-odu-pwr", - "InputConfig": { - "Address": 9000, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "001" - }, - { - "ChannelName": "hp-odu-pwr", - "InputConfig": { - "Address": 9002, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "001" - }, - { - "ChannelName": "store-pump-pwr", - "InputConfig": { - "Address": 9014, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "001" - }, - { - "ChannelName": "elt1-pwr", - "InputConfig": { + "EgaugeRegisterConfig": { "Address": 9006, "Denominator": 1, "Description": "change in value", @@ -136,10 +112,12 @@ "Unit": "W", "Version": "000" }, - "TypeName": "egauge.io", - "Version": "001" - } + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000" + } ], + "HwUid": "1001ab", "ModbusHost": "eGauge6069.local", "ModbusPort": 502, "TypeName": "electric.meter.component.gt", diff --git a/tests/types/test_ads111x_based_component_gt.py b/tests/types/test_ads111x_based_component_gt.py index 2545e9e4..996735e0 100644 --- a/tests/types/test_ads111x_based_component_gt.py +++ b/tests/types/test_ads111x_based_component_gt.py @@ -7,7 +7,6 @@ def test_ads111x_based_component_gt_generated() -> None: d = { "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", - "DisplayName": "GridWorks 12-Channel Ads-1115 based I2c Temp Sensor", "OpenVoltageByAds": [4.95, 4.95, 4.95], "ConfigList": [ diff --git a/tests/types/test_ads_channel_config.py b/tests/types/test_ads_channel_config.py index 04918312..00d4860a 100644 --- a/tests/types/test_ads_channel_config.py +++ b/tests/types/test_ads_channel_config.py @@ -32,14 +32,9 @@ def test_ads_channel_config_generated() -> None: assert type(d2["ThermistorMakeModel"]) is str d2 = dict(d, ThermistorMakeModel="unknown_enum_thing") - assert ( - AdsChannelConfig(**d2).ThermistorMakeModel == MakeModel.default() - ) + assert AdsChannelConfig(**d2).ThermistorMakeModel == MakeModel.default() assert type(d2["DataProcessingMethod"]) is str d2 = dict(d, DataProcessingMethod="unknown_enum_thing") - assert ( - AdsChannelConfig(**d2).DataProcessingMethod - == ThermistorDataMethod.default() - ) + assert AdsChannelConfig(**d2).DataProcessingMethod == ThermistorDataMethod.default() diff --git a/tests/types/test_egauge_io.py b/tests/types/test_egauge_io.py deleted file mode 100644 index 6c328c23..00000000 --- a/tests/types/test_egauge_io.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests egauge.io type, version 000""" - -from gwproto.types import EgaugeIo - - -def test_egauge_io_generated() -> None: - d = { - "ChannelName": "hp-idu-pwr", - "InputConfig": { - "Address": 9004, - "Name": "Garage power", - "Description": "", - "Type": "f32", - "Denominator": 1, - "Unit": "W", - "TypeName": "egauge.register.config", - "Version": "000", - }, - "TypeName": "egauge.io", - "Version": "001", - } - assert EgaugeIo.model_validate(d).model_dump() == d diff --git a/tests/types/test_electric_meter_channel_config.py b/tests/types/test_electric_meter_channel_config.py new file mode 100644 index 00000000..beb9047f --- /dev/null +++ b/tests/types/test_electric_meter_channel_config.py @@ -0,0 +1,28 @@ +"""Tests electric.meter.config type, version 000""" + +from gwproto.types import ElectricMeterChannelConfig + + +def test_electric_meter_channel_config_generated() -> None: + d = { + "AsyncCapture": True, + "AsyncCaptureDelta": 200, + "CapturePeriodS": 300, + "ChannelName": "hp-odu-pwr", + "Exponent": 0, + "PollPeriodMs": 1000, + "Unit": "W", + "EgaugeRegisterConfig": { + "Address": 9004, + "Name": "Garage power", + "Description": "", + "Type": "f32", + "Denominator": 1, + "Unit": "W", + "TypeName": "egauge.register.config", + "Version": "000", + }, + "TypeName": "electric.meter.channel.config", + "Version": "000", + } + assert ElectricMeterChannelConfig.model_validate(d).model_dump() == d diff --git a/tests/types/test_electric_meter_component_gt.py b/tests/types/test_electric_meter_component_gt.py index 9da2dc95..463fe69e 100644 --- a/tests/types/test_electric_meter_component_gt.py +++ b/tests/types/test_electric_meter_component_gt.py @@ -17,71 +17,7 @@ def test_electric_meter_component_gt_generated() -> None: "ChannelName": "hp-odu-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 50, - "CapturePeriodS": 300, - "ChannelName": "hp-idu-pwr", - "Exponent": 0, - "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 10, - "CapturePeriodS": 300, - "ChannelName": "primary-pump-pwr", - "Exponent": 0, - "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 10, - "CapturePeriodS": 300, - "ChannelName": "dist-pump-pwr", - "Exponent": 0, - "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 10, - "CapturePeriodS": 300, - "ChannelName": "store-pump-pwr", - "Exponent": 0, - "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - { - "AsyncCapture": True, - "AsyncCaptureDelta": 10, - "CapturePeriodS": 300, - "ChannelName": "oil-boiler-pwr", - "Exponent": 0, - "PollPeriodMs": 1000, - "TypeName": "channel.config", - "Unit": "W", - "Version": "000", - }, - ], - "DisplayName": "eGauge6069.local", - "EgaugeIoList": [ - { - "ChannelName": "hp-odu-pwr", - "InputConfig": { + "EgaugeRegisterConfig": { "Address": 9006, "Denominator": 1, "Description": "change in value", @@ -91,12 +27,18 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, { + "AsyncCapture": True, + "AsyncCaptureDelta": 50, + "CapturePeriodS": 300, "ChannelName": "hp-idu-pwr", - "InputConfig": { + "Exponent": 0, + "PollPeriodMs": 1000, + "EgaugeRegisterConfig": { "Address": 9000, "Denominator": 1, "Description": "change in value", @@ -106,12 +48,18 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, "ChannelName": "primary-pump-pwr", - "InputConfig": { + "Exponent": 0, + "PollPeriodMs": 1000, + "EgaugeRegisterConfig": { "Address": 9012, "Denominator": 1, "Description": "change in value", @@ -121,12 +69,18 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, "ChannelName": "dist-pump-pwr", - "InputConfig": { + "Exponent": 0, + "PollPeriodMs": 1000, + "EgaugeRegisterConfig": { "Address": 9010, "Denominator": 1, "Description": "change in value", @@ -136,12 +90,18 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, "ChannelName": "store-pump-pwr", - "InputConfig": { + "Exponent": 0, + "PollPeriodMs": 1000, + "EgaugeRegisterConfig": { "Address": 9014, "Denominator": 1, "Description": "change in value", @@ -151,12 +111,18 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, { + "AsyncCapture": True, + "AsyncCaptureDelta": 10, + "CapturePeriodS": 300, "ChannelName": "oil-boiler-pwr", - "InputConfig": { + "Exponent": 0, + "PollPeriodMs": 1000, + "EgaugeRegisterConfig": { "Address": 9016, "Denominator": 1, "Description": "change in value", @@ -166,10 +132,12 @@ def test_electric_meter_component_gt_generated() -> None: "Unit": "W", "Version": "000", }, - "TypeName": "egauge.io", - "Version": "001", + "TypeName": "electric.meter.channel.config", + "Unit": "W", + "Version": "000", }, ], + "DisplayName": "eGauge6069.local", "HwUid": "BP01349", "ModbusHost": "eGauge6069.local", "ModbusPort": 502, From 484f19f0819add4664bab9b3eec8fb37f277a8fa Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 08:18:51 -0400 Subject: [PATCH 141/168] add more axiom checks to hardware layout --- src/gwproto/data_classes/hardware_layout.py | 49 ++++++++++++++++++++- src/gwproto/types/egauge_register_config.py | 7 --- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index fbf5d870..7cbc22ff 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -22,6 +22,7 @@ from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) +from gwproto.types import ElectricMeterComponentGt from gwproto.data_classes.data_channel import DataChannel from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode @@ -220,9 +221,30 @@ def check_dc_id_uniqueness( if dupes: raise DcError(f"Duplicate dc.Id(s) found: {dupes}") + @classmethod + def check_node_channel_consistency( + cls, + nodes: dict[str, ShNode], + data_channels: dict[str, DataChannel] + ) -> None: + capturing_classes = [ + ActorClass.PowerMeter, + ActorClass.MultipurposeSensor, + ] + active_nodes = [node for node in nodes.values() if node.ActorClass in capturing_classes] + for node in active_nodes: + c: ComponentGt = node.component.gt + my_channel_names = [config.ChannelName for config in c.ConfigList] + my_channels = [dc for dc in data_channels.values() if dc.Name in my_channel_names] + for channel in my_channels: + if channel.CapturedByNodeName != node.Alias: + raise DcError(f"Channel {channel} should have CapturedByNodeName {node.Alias}") + + @classmethod def check_data_channel_consistency( cls, + nodes: dict[str, ShNode], components: dict[str, Component], data_channels: dict[str, DataChannel], ) -> None: @@ -246,9 +268,25 @@ def check_data_channel_consistency( f"From Components:{by_comp}\n" f"From DataChannel list:{actual}\n" ) + cls.check_node_channel_consistency(nodes, data_channels) + + @classmethod + def check_actor_component_consistency(cls, nodes: dict[str,ShNode]) -> None: + pm_nodes = [node for node in nodes.values() if node.ActorClass == ActorClass.PowerMeter] + for node in pm_nodes: + if node.component.gt.TypeName != "electric.meter.component.gt": + raise DcError(f"Power Meter node {node} needs ElectricMeterComponent." + f"Got {node.component.gt}") + em_nodes = [node for node in nodes.values() if node.ActorClass == ActorClass.MultipurposeSensor] + for node in em_nodes: + multi_comp_type_names = ["ads111x.based.component.gt"] + if node.component.gt.TypeName not in multi_comp_type_names: + raise DcError(f"Power Meter node {node} needs Compeont " + f"in {multi_comp_type_names}. Got " + f"{node.component.gt}") @classmethod - def check_node_consistency(cls, nodes: dict[str, ShNode]) -> None: + def check_node_unique_ids(cls, nodes: dict[str, ShNode]) -> None: id_counter = Counter(node.ShNodeId for node in nodes.values()) dupes = [node_id for node_id, count in id_counter.items() if count > 1] if dupes: @@ -443,13 +481,20 @@ def load_dict( # noqa: PLR0913 errors=errors, ) try: - cls.check_node_consistency(nodes) + cls.check_node_unique_ids(nodes) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append(LoadError("hardware.layout", nodes, e)) + try: + cls.check_actor_component_consistency(nodes) except Exception as e: # noqa: PERF203 if raise_errors: raise errors.append(LoadError("hardware.layout", nodes, e)) try: cls.check_data_channel_consistency( + nodes, components, data_channels, ) diff --git a/src/gwproto/types/egauge_register_config.py b/src/gwproto/types/egauge_register_config.py index eb711266..e3365c2c 100644 --- a/src/gwproto/types/egauge_register_config.py +++ b/src/gwproto/types/egauge_register_config.py @@ -6,13 +6,6 @@ class EgaugeRegisterConfig(BaseModel): - """ - Used to translate eGauge's Modbus Map. - - This type captures the information provided by eGauge in its modbus csv map, when reading - current, power, energy, voltage, frequency etc from an eGauge 4030. - """ - Address: int = Field( title="Address", description=( From 0175134cb44d5fa2b8ec0cf0f76fa4038a69af1b Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 09:16:30 -0400 Subject: [PATCH 142/168] EgaugeRegisterConfig can be missing from ElectricMeterChannelConfig --- src/gwproto/types/electric_meter_channel_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gwproto/types/electric_meter_channel_config.py b/src/gwproto/types/electric_meter_channel_config.py index 9903d130..552da93c 100644 --- a/src/gwproto/types/electric_meter_channel_config.py +++ b/src/gwproto/types/electric_meter_channel_config.py @@ -4,11 +4,11 @@ from gwproto.types.channel_config import ChannelConfig from gwproto.types.egauge_register_config import ( - EgaugeRegisterConfig, + EgaugeRegisterConfig as EgaugeConfig, ) class ElectricMeterChannelConfig(ChannelConfig): - EgaugeRegisterConfig: Optional[EgaugeRegisterConfig] + EgaugeRegisterConfig: Optional[EgaugeConfig] = None TypeName: Literal["electric.meter.channel.config"] = "electric.meter.channel.config" Version: Literal["000"] = "000" From c0ef10f69d97dbf913511714abec31eaa26a6266 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 09:48:16 -0400 Subject: [PATCH 143/168] Add channel name to hubitat attributes --- src/gwproto/types/hubitat_poller_gt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gwproto/types/hubitat_poller_gt.py b/src/gwproto/types/hubitat_poller_gt.py index a0e8cdcb..09912f08 100644 --- a/src/gwproto/types/hubitat_poller_gt.py +++ b/src/gwproto/types/hubitat_poller_gt.py @@ -6,6 +6,7 @@ class MakerAPIAttributeGt(BaseModel): attribute_name: str + channel_name: str node_name: str telemetry_name: TelemetryName = TelemetryName.WaterTempCTimes1000 unit: Unit = Unit.Celcius From a624d7aa7c0a38258740869c7576d4d4fb9abb0a Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 10:34:00 -0400 Subject: [PATCH 144/168] HubitatComponent stub has empty ConfigList --- src/gwproto/types/hubitat_component_gt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gwproto/types/hubitat_component_gt.py b/src/gwproto/types/hubitat_component_gt.py index e168fe61..70577eed 100644 --- a/src/gwproto/types/hubitat_component_gt.py +++ b/src/gwproto/types/hubitat_component_gt.py @@ -38,6 +38,7 @@ def make_stub(cls, component_id: str) -> "HubitatComponentGt": AccessToken="", MacAddress=MacAddress("00:00:00:00:00:00"), ), + ConfigList=[], ) From 0f21afc7de9e958c7bd097ff4eab7899e8bada33 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 11:02:40 -0400 Subject: [PATCH 145/168] fix all_power_meter_telemetry_tuples --- src/gwproto/data_classes/hardware_layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 7cbc22ff..a715652d 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -638,9 +638,9 @@ def all_nodes_in_agg_power_metering(self) -> List[ShNode]: def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: return [ TelemetryTuple( - AboutNode=self.node(config.AboutNodeName), + AboutNode=self.nodes[self.data_channels[config.ChannelName].AboutNodeName], SensorNode=self.power_meter_node, - TelemetryName=config.TelemetryName, + TelemetryName=self.data_channels[config.ChannelName].TelemetryName, ) for config in self.power_meter_component.gt.ConfigList ] From 28323122470727bb32bbdecda554b4647ae72b2f Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 11:11:24 -0400 Subject: [PATCH 146/168] enums are strings --- src/gwproto/data_classes/telemetry_tuple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index e3c069fa..ae8f5c1d 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -10,4 +10,4 @@ class TelemetryTuple(NamedTuple): TelemetryName: TelemetryName def __repr__(self) -> str: - return f"TT({self.AboutNode.alias} {self.TelemetryName.value} read by {self.SensorNode.alias})" + return f"TT({self.AboutNode.alias} {self.TelemetryName} read by {self.SensorNode.alias})" From 854461fbb03385adb04e2d846ea4defd0ec6a902 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 11:30:14 -0400 Subject: [PATCH 147/168] change format for GtShTelemetryFromMultipurposeSensor --- .../gt_sh_telemetry_from_multipurpose_sensor.py | 4 ++-- ...st_gt_sh_telemetry_from_multipurpose_sensor.py | 15 --------------- 2 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py diff --git a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py index 7ad1d272..f071fc70 100644 --- a/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py +++ b/src/gwproto/types/gt_sh_telemetry_from_multipurpose_sensor.py @@ -5,12 +5,12 @@ from pydantic import BaseModel, model_validator from gwproto.enums import TelemetryName -from gwproto.property_format import LeftRightDotStr, UTCMilliseconds +from gwproto.property_format import SpaceheatName, UTCMilliseconds class GtShTelemetryFromMultipurposeSensor(BaseModel): ScadaReadTimeUnixMs: UTCMilliseconds - AboutNodeAliasList: list[LeftRightDotStr] + AboutNodeAliasList: list[SpaceheatName] TelemetryNameList: List[TelemetryName] ValueList: List[int] TypeName: Literal["gt.sh.telemetry.from.multipurpose.sensor"] = ( diff --git a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py b/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py deleted file mode 100644 index a60f819a..00000000 --- a/tests/types/test_gt_sh_telemetry_from_multipurpose_sensor.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Tests gt.sh.telemetry.from.multipurpose.sensor type, version 100""" - -from gwproto.types import GtShTelemetryFromMultipurposeSensor - - -def test_gt_sh_telemetry_from_multipurpose_sensor_generated() -> None: - d = { - "ScadaReadTimeUnixMs": 1656587343297, - "AboutNodeAliasList": ["a.elt1"], - "TelemetryNameList": ["PowerW"], - "ValueList": [18000], - "TypeName": "gt.sh.telemetry.from.multipurpose.sensor", - "Version": "100", - } - assert GtShTelemetryFromMultipurposeSensor.model_validate(d).model_dump() == d From 45847b6838e7ff47207e1c96dfc85518e5ceeae5 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 25 Sep 2024 12:26:11 -0400 Subject: [PATCH 148/168] Make ChannelConfigs hashable --- src/gwproto/types/channel_config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gwproto/types/channel_config.py b/src/gwproto/types/channel_config.py index 10a29da1..4aa1dab7 100644 --- a/src/gwproto/types/channel_config.py +++ b/src/gwproto/types/channel_config.py @@ -24,6 +24,9 @@ class ChannelConfig(BaseModel): model_config = ConfigDict(use_enum_values=True) + def __hash__(self) -> int: + return hash(self.ChannelName) + @model_validator(mode="after") def check_axiom_1(self) -> Self: """ From 89729409065c1091316700561bd62f920cfd7a64 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 25 Sep 2024 14:38:48 -0400 Subject: [PATCH 149/168] Fix HardwareLayout.cac() for case where node has no component --- src/gwproto/data_classes/hardware_layout.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index cc98c3f0..c20869e2 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -329,7 +329,10 @@ def component(self, node_alias: str) -> Optional[Component]: return self.component_from_node(self.node(node_alias, None)) def cac(self, node_alias: str) -> Optional[ComponentAttributeClassGt]: - return self.component(node_alias).cac + component = self.component(node_alias) + if component is None: + return None + return component.cac def get_component_as_type(self, component_id: str, type_: Type[T]) -> Optional[T]: component = self.components.get(component_id, None) From 1e0d0d00538ed2890a971004a1ff3c40d3fde1e3 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Wed, 25 Sep 2024 14:57:22 -0400 Subject: [PATCH 150/168] Make ruff pass by extracting validation inside load_dict() into its own routine --- src/gwproto/data_classes/hardware_layout.py | 152 ++++++++++++-------- 1 file changed, 93 insertions(+), 59 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 24995e84..4f616780 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -22,7 +22,6 @@ from gwproto.data_classes.components.electric_meter_component import ( ElectricMeterComponent, ) -from gwproto.types import ElectricMeterComponentGt from gwproto.data_classes.data_channel import DataChannel from gwproto.data_classes.resolver import ComponentResolver from gwproto.data_classes.sh_node import ShNode @@ -50,6 +49,13 @@ class LoadError: exception: Exception +class LoadArgs(typing.TypedDict): + cacs: dict[str, ComponentAttributeClassGt] + components: dict[str, Component] + nodes: dict[str, ShNode] + data_channels: dict[str, DataChannel] + + class HardwareLayout: layout: dict[Any, Any] cacs: dict[str, ComponentAttributeClassGt] @@ -223,23 +229,26 @@ def check_dc_id_uniqueness( @classmethod def check_node_channel_consistency( - cls, - nodes: dict[str, ShNode], - data_channels: dict[str, DataChannel] + cls, nodes: dict[str, ShNode], data_channels: dict[str, DataChannel] ) -> None: capturing_classes = [ ActorClass.PowerMeter, ActorClass.MultipurposeSensor, ] - active_nodes = [node for node in nodes.values() if node.ActorClass in capturing_classes] + active_nodes = [ + node for node in nodes.values() if node.ActorClass in capturing_classes + ] for node in active_nodes: c: ComponentGt = node.component.gt my_channel_names = [config.ChannelName for config in c.ConfigList] - my_channels = [dc for dc in data_channels.values() if dc.Name in my_channel_names] + my_channels = [ + dc for dc in data_channels.values() if dc.Name in my_channel_names + ] for channel in my_channels: if channel.CapturedByNodeName != node.Alias: - raise DcError(f"Channel {channel} should have CapturedByNodeName {node.Alias}") - + raise DcError( + f"Channel {channel} should have CapturedByNodeName {node.Alias}" + ) @classmethod def check_data_channel_consistency( @@ -269,21 +278,31 @@ def check_data_channel_consistency( f"From DataChannel list:{actual}\n" ) cls.check_node_channel_consistency(nodes, data_channels) - + @classmethod - def check_actor_component_consistency(cls, nodes: dict[str,ShNode]) -> None: - pm_nodes = [node for node in nodes.values() if node.ActorClass == ActorClass.PowerMeter] + def check_actor_component_consistency(cls, nodes: dict[str, ShNode]) -> None: + pm_nodes = [ + node for node in nodes.values() if node.ActorClass == ActorClass.PowerMeter + ] for node in pm_nodes: if node.component.gt.TypeName != "electric.meter.component.gt": - raise DcError(f"Power Meter node {node} needs ElectricMeterComponent." - f"Got {node.component.gt}") - em_nodes = [node for node in nodes.values() if node.ActorClass == ActorClass.MultipurposeSensor] + raise DcError( + f"Power Meter node {node} needs ElectricMeterComponent." + f"Got {node.component.gt}" + ) + em_nodes = [ + node + for node in nodes.values() + if node.ActorClass == ActorClass.MultipurposeSensor + ] for node in em_nodes: multi_comp_type_names = ["ads111x.based.component.gt"] if node.component.gt.TypeName not in multi_comp_type_names: - raise DcError(f"Power Meter node {node} needs Compeont " - f"in {multi_comp_type_names}. Got " - f"{node.component.gt}") + raise DcError( + f"Power Meter node {node} needs Compeont " + f"in {multi_comp_type_names}. Got " + f"{node.component.gt}" + ) @classmethod def check_node_unique_ids(cls, nodes: dict[str, ShNode]) -> None: @@ -429,6 +448,58 @@ def load( # noqa: PLR0913 component_decoder=component_decoder, ) + @classmethod + def validate_layout( + cls, + load_args: LoadArgs, + *, + raise_errors: bool, + errors: Optional[list[LoadError]], + ) -> None: + nodes = load_args["nodes"] + components = load_args["components"] + data_channels = load_args["data_channels"] + try: + cls.check_node_unique_ids(nodes) + except Exception as e: + if raise_errors: + raise + errors.append(LoadError("hardware.layout", nodes, e)) + try: + cls.check_actor_component_consistency(nodes) + except Exception as e: + if raise_errors: + raise + errors.append(LoadError("hardware.layout", nodes, e)) + try: + cls.check_data_channel_consistency( + nodes, + components, + data_channels, + ) + cls.check_transactive_metering_consistency( + nodes, + data_channels, + ) + except Exception as e: + if raise_errors: + raise + errors.append(LoadError("data.channel.gt", data_channels, e)) + ads111x_components = [ + comp + for comp in components.values() + if isinstance(comp, Ads111xBasedComponent) + ] + for c in ads111x_components: + try: + cls.check_ads_terminal_block_consistency(c) + except Exception as e: # noqa: PERF203 + if raise_errors: + raise + errors.append( + LoadError("ads111x.based.component.gt", c.gt.model_dump(), e) + ) + @classmethod def load_dict( # noqa: PLR0913 cls, @@ -468,7 +539,7 @@ def load_dict( # noqa: PLR0913 raise_errors=raise_errors, errors=errors, ) - load_args = { + load_args: LoadArgs = { "cacs": cacs, "components": components, "nodes": nodes, @@ -480,46 +551,7 @@ def load_dict( # noqa: PLR0913 raise_errors=raise_errors, errors=errors, ) - try: - cls.check_node_unique_ids(nodes) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError("hardware.layout", nodes, e)) - try: - cls.check_actor_component_consistency(nodes) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError("hardware.layout", nodes, e)) - try: - cls.check_data_channel_consistency( - nodes, - components, - data_channels, - ) - cls.check_transactive_metering_consistency( - nodes, - data_channels, - ) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append(LoadError("data.channel.gt", layout, e)) - ads111x_components = [ - comp - for comp in components.values() - if isinstance(comp, Ads111xBasedComponent) - ] - for c in ads111x_components: - try: - cls.check_ads_terminal_block_consistency(c) - except Exception as e: # noqa: PERF203 - if raise_errors: - raise - errors.append( - LoadError("ads111x.based.component.gt", c.gt.model_dump(), e) - ) + cls.validate_layout(load_args, raise_errors=raise_errors, errors=errors) return HardwareLayout(layout, **load_args) def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 @@ -641,7 +673,9 @@ def all_nodes_in_agg_power_metering(self) -> List[ShNode]: def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: return [ TelemetryTuple( - AboutNode=self.nodes[self.data_channels[config.ChannelName].AboutNodeName], + AboutNode=self.nodes[ + self.data_channels[config.ChannelName].AboutNodeName + ], SensorNode=self.power_meter_node, TelemetryName=self.data_channels[config.ChannelName].TelemetryName, ) From f9422e7808ea91a2eae4a417f1284976729c2ef2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:03:42 +0000 Subject: [PATCH 151/168] build(deps): bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.2 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.0 to 1.10.2. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.0...v1.10.2) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b189b64..b385c797 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,14 +57,14 @@ jobs: - name: Publish package on PyPI if: steps.check-version.outputs.tag - uses: pypa/gh-action-pypi-publish@v1.10.0 + uses: pypa/gh-action-pypi-publish@v1.10.2 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} - name: Publish package on TestPyPI if: "! steps.check-version.outputs.tag" - uses: pypa/gh-action-pypi-publish@v1.10.0 + uses: pypa/gh-action-pypi-publish@v1.10.2 with: user: __token__ password: ${{ secrets.TEST_PYPI_TOKEN }} From 7686be51967b528cbbbe410ee8846acef99a243a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:25:48 +0000 Subject: [PATCH 152/168] build(deps-dev): bump virtualenv from 20.26.5 to 20.26.6 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.5 to 20.26.6. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.26.5...20.26.6) --- updated-dependencies: - dependency-name: virtualenv dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index f351e97e..b053b337 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1781,13 +1781,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.26.5" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] From 6a852be444df67ade74198ec5ef0236eb2926e81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:26:13 +0000 Subject: [PATCH 153/168] build(deps-dev): bump websockets from 13.0.1 to 13.1 Bumps [websockets](https://github.com/python-websockets/websockets) from 13.0.1 to 13.1. - [Release notes](https://github.com/python-websockets/websockets/releases) - [Commits](https://github.com/python-websockets/websockets/compare/13.0.1...13.1) --- updated-dependencies: - dependency-name: websockets dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 174 ++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/poetry.lock b/poetry.lock index f351e97e..38530c71 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1896,97 +1896,97 @@ anyio = ">=3.0.0" [[package]] name = "websockets" -version = "13.0.1" +version = "13.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" files = [ - {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"}, - {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"}, - {file = "websockets-13.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448"}, - {file = "websockets-13.0.1-cp310-cp310-win32.whl", hash = "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3"}, - {file = "websockets-13.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df"}, - {file = "websockets-13.0.1-cp311-cp311-win32.whl", hash = "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f"}, - {file = "websockets-13.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237"}, - {file = "websockets-13.0.1-cp312-cp312-win32.whl", hash = "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185"}, - {file = "websockets-13.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2"}, - {file = "websockets-13.0.1-cp313-cp313-win32.whl", hash = "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb"}, - {file = "websockets-13.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb"}, - {file = "websockets-13.0.1-cp38-cp38-win32.whl", hash = "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4"}, - {file = "websockets-13.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d"}, - {file = "websockets-13.0.1-cp39-cp39-win32.whl", hash = "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58"}, - {file = "websockets-13.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980"}, - {file = "websockets-13.0.1-py3-none-any.whl", hash = "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817"}, - {file = "websockets-13.0.1.tar.gz", hash = "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, + {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, + {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, + {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, + {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, + {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, + {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, + {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, + {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, + {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, + {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, + {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, + {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, + {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, + {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, + {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, + {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, + {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, + {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, + {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, + {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, + {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, + {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, + {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, ] [[package]] From 9bdb5bb9e642b2cdf395234cf64fe8e2600c70f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:26:40 +0000 Subject: [PATCH 154/168] build(deps-dev): bump ruff from 0.6.6 to 0.6.8 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.6 to 0.6.8. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.6...0.6.8) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index f351e97e..add2b0ca 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1435,29 +1435,29 @@ files = [ [[package]] name = "ruff" -version = "0.6.6" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.6-py3-none-linux_armv6l.whl", hash = "sha256:f5bc5398457484fc0374425b43b030e4668ed4d2da8ee7fdda0e926c9f11ccfb"}, - {file = "ruff-0.6.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:515a698254c9c47bb84335281a170213b3ee5eb47feebe903e1be10087a167ce"}, - {file = "ruff-0.6.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6bb1b4995775f1837ab70f26698dd73852bbb82e8f70b175d2713c0354fe9182"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c546f412dfae8bb9cc4f27f0e45cdd554e42fecbb34f03312b93368e1cd0a6"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:59627e97364329e4eae7d86fa7980c10e2b129e2293d25c478ebcb861b3e3fd6"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94c3f78c3d32190aafbb6bc5410c96cfed0a88aadb49c3f852bbc2aa9783a7d8"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:704da526c1e137f38c8a067a4a975fe6834b9f8ba7dbc5fd7503d58148851b8f"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efeede5815a24104579a0f6320660536c5ffc1c91ae94f8c65659af915fb9de9"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e368aef0cc02ca3593eae2fb8186b81c9c2b3f39acaaa1108eb6b4d04617e61f"}, - {file = "ruff-0.6.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2653fc3b2a9315bd809725c88dd2446550099728d077a04191febb5ea79a4f79"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bb858cd9ce2d062503337c5b9784d7b583bcf9d1a43c4df6ccb5eab774fbafcb"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:488f8e15c01ea9afb8c0ba35d55bd951f484d0c1b7c5fd746ce3c47ccdedce68"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:aefb0bd15f1cfa4c9c227b6120573bb3d6c4ee3b29fb54a5ad58f03859bc43c6"}, - {file = "ruff-0.6.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a4c0698cc780bcb2c61496cbd56b6a3ac0ad858c966652f7dbf4ceb029252fbe"}, - {file = "ruff-0.6.6-py3-none-win32.whl", hash = "sha256:aadf81ddc8ab5b62da7aae78a91ec933cbae9f8f1663ec0325dae2c364e4ad84"}, - {file = "ruff-0.6.6-py3-none-win_amd64.whl", hash = "sha256:0adb801771bc1f1b8cf4e0a6fdc30776e7c1894810ff3b344e50da82ef50eeb1"}, - {file = "ruff-0.6.6-py3-none-win_arm64.whl", hash = "sha256:4b4d32c137bc781c298964dd4e52f07d6f7d57c03eae97a72d97856844aa510a"}, - {file = "ruff-0.6.6.tar.gz", hash = "sha256:0fc030b6fd14814d69ac0196396f6761921bd20831725c7361e1b8100b818034"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] From 18d74692f8afa955934b49444c49f5ac70111d2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:26:58 +0000 Subject: [PATCH 155/168] build(deps-dev): bump starlette from 0.38.5 to 0.39.2 Bumps [starlette](https://github.com/encode/starlette) from 0.38.5 to 0.39.2. - [Release notes](https://github.com/encode/starlette/releases) - [Changelog](https://github.com/encode/starlette/blob/master/docs/release-notes.md) - [Commits](https://github.com/encode/starlette/compare/0.38.5...0.39.2) --- updated-dependencies: - dependency-name: starlette dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index f351e97e..600422b0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1678,13 +1678,13 @@ test = ["pytest"] [[package]] name = "starlette" -version = "0.38.5" +version = "0.39.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.5-py3-none-any.whl", hash = "sha256:632f420a9d13e3ee2a6f18f437b0a9f1faecb0bc42e1942aa2ea0e379a4c4206"}, - {file = "starlette-0.38.5.tar.gz", hash = "sha256:04a92830a9b6eb1442c766199d62260c3d4dc9c4f9188360626b1e0273cb7077"}, + {file = "starlette-0.39.2-py3-none-any.whl", hash = "sha256:134dd6deb655a9775991d352312d53f1879775e5cc8a481f966e83416a2c3f71"}, + {file = "starlette-0.39.2.tar.gz", hash = "sha256:caaa3b87ef8518ef913dac4f073dea44e85f73343ad2bdc17941931835b2a26a"}, ] [package.dependencies] From fec1910baa6ffa85cc4792cb540a0abdaeb95d1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:59:42 +0000 Subject: [PATCH 156/168] build(deps): bump virtualenv in /.github/workflows Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.3 to 20.26.6. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.26.3...20.26.6) --- updated-dependencies: - dependency-name: virtualenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt index 127ea4da..ad5df5db 100644 --- a/.github/workflows/constraints.txt +++ b/.github/workflows/constraints.txt @@ -2,4 +2,4 @@ pip==24.2 nox==2024.4.15 nox-poetry==1.0.3 poetry==1.8.3 -virtualenv==20.26.3 +virtualenv==20.26.6 From 1d0cf5db3f297d734a4fde731cc33ef48c8017b6 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 1 Oct 2024 13:40:30 -0400 Subject: [PATCH 157/168] Update test hardware layout --- .../GridworksCore/Types/DeriveTypes.xslt | 21 ++- tests/config/hardware-layout.json | 136 ++++++++++++------ 2 files changed, 104 insertions(+), 53 deletions(-) diff --git a/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt b/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt index 5299be4a..b82b743f 100644 --- a/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt +++ b/CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt @@ -96,6 +96,15 @@ from pydantic import BaseModel , ConfigDict + + +, StrictInt + @@ -199,16 +208,6 @@ from gwproto.property_format import ( ) - - -from gwproto.property_format import ReallyAnInt - - class @@ -306,7 +305,7 @@ class PositiveInt - ReallyAnInt + StrictInt diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index 03615e1e..ff712a07 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -39,16 +39,6 @@ "ChannelName": "hp-odu-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "EgaugeRegisterConfig": { - "Address": 9000, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" @@ -60,16 +50,6 @@ "ChannelName": "hp-idu-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "EgaugeRegisterConfig": { - "Address": 9002, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" @@ -81,16 +61,6 @@ "ChannelName": "store-pump-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "EgaugeRegisterConfig": { - "Address": 9014, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" @@ -102,16 +72,6 @@ "ChannelName": "elt1-pwr", "Exponent": 0, "PollPeriodMs": 1000, - "EgaugeRegisterConfig": { - "Address": 9006, - "Denominator": 1, - "Description": "change in value", - "Name": "", - "Type": "f32", - "TypeName": "egauge.register.config", - "Unit": "W", - "Version": "000" - }, "TypeName": "electric.meter.channel.config", "Unit": "W", "Version": "000" @@ -328,9 +288,24 @@ } ], "OtherComponents": [ + { + "ComponentAttributeClassId": "62528da5-b510-4ac2-82c1-3782842eae07", + "ComponentId": "ab7bc99c-08ed-489e-b760-e36b57653e7e", + "DisplayName": "Hubitat 92:22:AB", + "Hubitat": { + "AccessToken": "f5a8d2ba-c155-4484-8b43-70599bc9037c", + "Host": "hubitat-orange.local", + "MacAddress": "34:E1:D1:92:22:AB", + "MakerApiId": 1, + "WebListenEnabled": true + }, + "ConfigList": [], + "TypeName": "hubitat.component.gt", + "Version": "001" + }, { "ComponentId": "2c302eed-2f86-4ed6-8019-df31c22e1704", - "DisplayName": "Downstairs T6 Thermostat", + "DisplayName": "Downstairs Thermostat", "ComponentAttributeClassId": "03533a1f-3cb9-4a1f-8d57-690c0ad0475b", "ConfigList": [ { @@ -365,7 +340,74 @@ "Version": "000" } ], - "TypeName": "component.gt", + "Poller": { + "Attributes": [ + { + "AttributeName": "temperature", + "Enabled": true, + "InterpretAsNumber": true, + "NodeName": "zone1-down", + "ReportMissing": true, + "ReportParseError": true, + "TelemetryName": "AirTempFTimes1000", + "WebListenEnabled": true, + "WebPollEnabled": true, + "AsyncCapture": false, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-temp", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "Fahrenheit", + "Version": "000" + }, + { + "AttributeName": "heatingSetpoint", + "Enabled": true, + "InterpretAsNumber": true, + "NodeName": "zone1-down-stat", + "ReportMissing": true, + "ReportParseError": true, + "TelemetryName": "AirTempFTimes1000", + "WebListenEnabled": true, + "WebPollEnabled": true, + "AsyncCapture": false, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-set", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "Fahrenheit", + "Version": "000" + }, + { + "AttributeName": "thermostatOperatingState", + "Enabled": true, + "InterpretAsNumber": true, + "NodeName": "zone1-down-stat", + "ReportMissing": true, + "ReportParseError": true, + "TelemetryName": "ThermostatState", + "WebListenEnabled": true, + "WebPollEnabled": true, + "AsyncCapture": true, + "AsyncCaptureDelta": 1, + "CapturePeriodS": 60, + "ChannelName": "zone1-down-state", + "Exponent": 3, + "PollPeriodMs": 1000, + "TypeName": "channel.config", + "Unit": "ThermostatStateEnum", + "Version": "000" + } + ], + "DeviceId": 164, + "Enabled": true, + "HubitatComponentId": "ab7bc99c-08ed-489e-b760-e36b57653e7e", + "PollPeriodSeconds": 60.0, + "WebListenEnabled": true + }, + "TypeName": "hubitat.poller.component.gt", "Version": "001" } ], @@ -613,6 +655,16 @@ "ShNodeId": "8c9c155c-5b9a-448c-8df3-2edd9f4ebdb7", "TypeName": "spaceheat.node.gt", "Version": "120" + }, + { + "ActorClass": "Hubitat", + "Alias": "hubitat", + "ComponentId": "ab7bc99c-08ed-489e-b760-e36b57653e7e", + "DisplayName": "Hubitat 81:15:21", + "Role": "Unknown", + "ShNodeId": "13d9b3cb-2b13-444c-9e03-f31203772b64", + "TypeName": "spaceheat.node.gt", + "Version": "120" } ], "DataChannels": [ From c89154d036fe0703ff2fc7bf2ba3bde46599fda9 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 1 Oct 2024 14:25:08 -0400 Subject: [PATCH 158/168] component_attribute_class -> cac --- src/gwproto/data_classes/hardware_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 4f616780..b099fe24 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -702,7 +702,7 @@ def power_meter_cac(self) -> ElectricMeterCacGt: f"ERROR. power_meter_component cac {self.power_meter_component.cac}" f" / {type(self.power_meter_component.cac)} is not an ElectricMeterCac" ) - return self.power_meter_node.component.component_attribute_class # type: ignore[union-attr, return-value] + return self.power_meter_node.component.cac # type: ignore[union-attr, return-value] @cached_property def all_resistive_heaters(self) -> List[ShNode]: From 3345068bfeb458c229a661dc5f8520dd4138138f Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 1 Oct 2024 15:06:35 -0400 Subject: [PATCH 159/168] fix TSNap make model --- src/gwproto/type_helpers/cacs_by_make_model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gwproto/type_helpers/cacs_by_make_model.py b/src/gwproto/type_helpers/cacs_by_make_model.py index 21eb75e6..27fc7448 100644 --- a/src/gwproto/type_helpers/cacs_by_make_model.py +++ b/src/gwproto/type_helpers/cacs_by_make_model.py @@ -6,6 +6,7 @@ MakeModel.EGAUGE__4030.value: "739a6e32-bb9c-43bc-a28d-fb61be665522", MakeModel.NCD__PR814SPST.value: "c6e736d8-8078-44f5-98bb-d72ca91dc773", MakeModel.ADAFRUIT__642.value: "43564cd2-0e78-41a2-8b67-ad80c02161e8", + MakeModel.GRIDWORKS__TSNAP1.value: "432073b8-4d2b-4e36-9229-73893f33f846", MakeModel.GRIDWORKS__WATERTEMPHIGHPRECISION.value: "7937eb7e-24d5-4d52-990f-cca063484df9", MakeModel.GRIDWORKS__SIMPM1.value: "28897ac1-ea42-4633-96d3-196f63f5a951", MakeModel.SCHNEIDERELECTRIC__IEM3455.value: "6bcdc388-de10-40e6-979a-8d66bfcfe9ba", @@ -19,7 +20,7 @@ MakeModel.AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN.value: "2821c81d-054d-4003-9b07-2c295aef40f5", MakeModel.YHDC__SCT013100.value: "812761ba-6544-4796-9aad-e1c979f58734", MakeModel.MAGNELAB__SCT0300050.value: "cf312bd6-7ca5-403b-a61b-b2e817ea1e22", - MakeModel.GRIDWORKS__MULTITEMP1.value: "432073b8-4d2b-4e36-9229-73893f33f846", + MakeModel.GRIDWORKS__MULTITEMP1.value: "b4db046e-500c-4ecd-86ed-a9dbaae039f9", MakeModel.KRIDA__EMR16I2CV3.value: "018d9ffb-89d1-4cc4-95c0-f170711b5ffa", MakeModel.OMEGA__FTB8007HWPT.value: "8cf6c726-e38a-4900-9cfe-ae6f053aafdf", MakeModel.ISTEC_4440.value: "62ed724c-ba62-4302-ae30-d52b20d42ad9", @@ -37,4 +38,5 @@ MakeModel.GRIDWORKS__SIMMULTITEMP.value: "627ac482-24fe-46b2-ba8c-3d6f1e1ee069", MakeModel.GRIDWORKS__SIMTOTALIZER.value: "a88f8f4c-fe1e-4645-a7f4-249912131dc8", MakeModel.KRIDA__DOUBLEEMR16I2CV3.value: "29eab8b1-100f-4230-bb44-3a2fcba33cc3", + MakeModel.GRIDWORKS__SIMDOUBLE16PINI2CRELAY.value: "44ff9d75-edb9-4249-afea-96822bfc55f3", } From 33bfa6bfb415a7a008251e5bf2a1d1d8ad6cf3e5 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 1 Oct 2024 15:43:39 -0400 Subject: [PATCH 160/168] Add ChannelStub --- src/gwproto/data_classes/telemetry_tuple.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index ae8f5c1d..70329985 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -1,7 +1,10 @@ from typing import NamedTuple +from pydantic import BaseModel + from gwproto.data_classes.sh_node import ShNode from gwproto.enums import TelemetryName +from gwproto.property_format import SpaceheatName class TelemetryTuple(NamedTuple): @@ -11,3 +14,13 @@ class TelemetryTuple(NamedTuple): def __repr__(self) -> str: return f"TT({self.AboutNode.alias} {self.TelemetryName} read by {self.SensorNode.alias})" + + +class ChannelStub(BaseModel): + Name: SpaceheatName + AboutNodeName: SpaceheatName + CapturedByNodeName: SpaceheatName + TelemetryName: TelemetryName + + def __hash__(self) -> int: + return hash(self.Name) From 56bce8e37540e671c5968336cde11602b25fd29e Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Tue, 1 Oct 2024 15:57:36 -0400 Subject: [PATCH 161/168] channel stub has InPowerMetering --- src/gwproto/data_classes/telemetry_tuple.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index 70329985..40e8b34b 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -21,6 +21,7 @@ class ChannelStub(BaseModel): AboutNodeName: SpaceheatName CapturedByNodeName: SpaceheatName TelemetryName: TelemetryName + InPowerMetering: bool = False def __hash__(self) -> int: return hash(self.Name) From 4540545c5361bbbe52beaec3420bb31ec645eed7 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Wed, 2 Oct 2024 09:43:28 -0400 Subject: [PATCH 162/168] HubitatGt in type_helpers, axiom for AsyncCapture Also, ChannelStub does not have CapturedByNodeName --- src/gwproto/data_classes/telemetry_tuple.py | 1 - src/gwproto/type_helpers/__init__.py | 2 ++ src/gwproto/types/channel_config.py | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index 40e8b34b..43e1126f 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -19,7 +19,6 @@ def __repr__(self) -> str: class ChannelStub(BaseModel): Name: SpaceheatName AboutNodeName: SpaceheatName - CapturedByNodeName: SpaceheatName TelemetryName: TelemetryName InPowerMetering: bool = False diff --git a/src/gwproto/type_helpers/__init__.py b/src/gwproto/type_helpers/__init__.py index b74bfb3e..e690cc17 100644 --- a/src/gwproto/type_helpers/__init__.py +++ b/src/gwproto/type_helpers/__init__.py @@ -1,5 +1,6 @@ from gwproto.type_helpers.cacs_by_make_model import CACS_BY_MAKE_MODEL from gwproto.types.hubitat_component_gt import HubitatRESTResolutionSettings +from gwproto.types.hubitat_gt import HubitatGt from gwproto.types.hubitat_poller_gt import HubitatPollerGt, MakerAPIAttributeGt from gwproto.types.hubitat_tank_gt import ( FibaroTempSensorSettings, @@ -21,6 +22,7 @@ "AioHttpClientTimeout", "FibaroTempSensorSettings", "FibaroTempSensorSettingsGt", + "HubitatGt", "HubitatPollerGt", "HubitatRESTResolutionSettings", "HubitatTankSettingsGt", diff --git a/src/gwproto/types/channel_config.py b/src/gwproto/types/channel_config.py index 4aa1dab7..ef0cb91e 100644 --- a/src/gwproto/types/channel_config.py +++ b/src/gwproto/types/channel_config.py @@ -33,7 +33,11 @@ def check_axiom_1(self) -> Self: Axiom 1: Async Capture Consistency. If AsyncCapture is True, then AsyncCaptureDelta exists """ - # Implement check for axiom 1" + if self.AsyncCapture and not self.AsyncCaptureDelta: + raise ValueError( + "Axiom 1 violated! If AsyncCapture is true, " + "then AsyncCaptureDelta must exist" + ) return self @model_validator(mode="after") From 7f8e259afbabbdd28c45b006a8fa37ed46807126 Mon Sep 17 00:00:00 2001 From: Andrew Schweitzer Date: Thu, 3 Oct 2024 12:55:37 -0400 Subject: [PATCH 163/168] TelemetrySnapshotSpaceheat uses SpaceheatName to validate AboutNodeAliasList --- .../types/telemetry_snapshot_spaceheat.py | 4 +-- tests/data/snapshot_message.json | 26 +++++++++---------- tests/types/test_snapshot_spaceheat.py | 2 +- .../test_telemetry_snapshot_spaceheat.py | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/gwproto/types/telemetry_snapshot_spaceheat.py b/src/gwproto/types/telemetry_snapshot_spaceheat.py index e459796b..2969a2f5 100644 --- a/src/gwproto/types/telemetry_snapshot_spaceheat.py +++ b/src/gwproto/types/telemetry_snapshot_spaceheat.py @@ -5,12 +5,12 @@ from pydantic import BaseModel from gwproto.enums import TelemetryName -from gwproto.property_format import LeftRightDotStr, UTCMilliseconds +from gwproto.property_format import SpaceheatName, UTCMilliseconds class TelemetrySnapshotSpaceheat(BaseModel): ReportTimeUnixMs: UTCMilliseconds - AboutNodeAliasList: list[LeftRightDotStr] + AboutNodeAliasList: list[SpaceheatName] ValueList: List[int] TelemetryNameList: list[TelemetryName] TypeName: Literal["telemetry.snapshot.spaceheat"] = "telemetry.snapshot.spaceheat" diff --git a/tests/data/snapshot_message.json b/tests/data/snapshot_message.json index 1563851b..14938403 100644 --- a/tests/data/snapshot_message.json +++ b/tests/data/snapshot_message.json @@ -12,19 +12,19 @@ "FromGNodeInstanceId": "28817671-3899-4e24-a337-abcb8633e47a", "Snapshot": { "AboutNodeAliasList": [ - "a.elt1.relay", - "a.tank.out.pump.relay", - "a.tank.out.pump.baseboard1.fan.relay", - "a.tank.out.temp1", - "a.tank.out.far.temp1", - "a.tank.in.temp1", - "a.tank.temp0", - "a.garage.temp1", - "a.elt1", - "a.tank.out.temp2", - "a.tank.in.temp2", - "a.garage.temp2", - "a.tankoutside.temp2" + "a-elt1-relay", + "a-tank-out-pump-relay", + "a-tank-out-pump-baseboard1-fan-relay", + "a-tank-out-temp1", + "a-tank-out-far-temp1", + "a-tank-in-temp1", + "a-tank-temp0", + "a-garage-temp1", + "a-elt1", + "a-tank-out-temp2", + "a-tank-in-temp2", + "a-garage-temp2", + "a-tankoutside-temp2" ], "ValueList": [ 0, 0, 0, 17812, 15250, 14312, 21812, 15062, 0, -44, -3626, -3015, 3041 diff --git a/tests/types/test_snapshot_spaceheat.py b/tests/types/test_snapshot_spaceheat.py index 82c1f421..0174abae 100644 --- a/tests/types/test_snapshot_spaceheat.py +++ b/tests/types/test_snapshot_spaceheat.py @@ -9,7 +9,7 @@ def test_snapshot_spaceheat_generated() -> None: "FromGNodeInstanceId": "0384ef21-648b-4455-b917-58a1172d7fc1", "Snapshot": { "TelemetryNameList": ["RelayState"], - "AboutNodeAliasList": ["a.elt1.relay"], + "AboutNodeAliasList": ["a-elt1-relay"], "ReportTimeUnixMs": 1656363448000, "ValueList": [1], "TypeName": "telemetry.snapshot.spaceheat", diff --git a/tests/types/test_telemetry_snapshot_spaceheat.py b/tests/types/test_telemetry_snapshot_spaceheat.py index ba36892f..c8a10dff 100644 --- a/tests/types/test_telemetry_snapshot_spaceheat.py +++ b/tests/types/test_telemetry_snapshot_spaceheat.py @@ -6,7 +6,7 @@ def test_telemetry_snapshot_spaceheat_generated() -> None: d = { "ReportTimeUnixMs": 1656363448000, - "AboutNodeAliasList": ["a.elt1.relay", "a.tank.temp0"], + "AboutNodeAliasList": ["a-elt1-relay", "a-tank-temp0"], "ValueList": [1, 66086], "TelemetryNameList": ["RelayState", "WaterTempCTimes1000"], "TypeName": "telemetry.snapshot.spaceheat", From 6f807a304412140c497da7b0271a86eb6262d537 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Fri, 4 Oct 2024 10:27:14 -0400 Subject: [PATCH 164/168] fix all_multipurpose_telemetry_tuples --- src/gwproto/data_classes/hardware_layout.py | 35 ++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index bec9e7d9..99097415 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -560,31 +560,30 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: multi_nodes = list( filter( lambda x: ( - ( - x.actor_class - in { - ActorClass.MultipurposeSensor, - ActorClass.HubitatTankModule, - ActorClass.HubitatPoller, - ActorClass.HoneywellThermostat, - } - ) - and hasattr(x.component, "config_list") + x.actor_class + in { + ActorClass.MultipurposeSensor, + ActorClass.HubitatTankModule, + ActorClass.HubitatPoller, + ActorClass.HoneywellThermostat, + } ), self.nodes.values(), ) ) telemetry_tuples = [] for node in multi_nodes: + channels = [ + self.data_channels[cfg.ChannelName] + for cfg in node.component.gt.ConfigList + ] telemetry_tuples.extend( - [ - TelemetryTuple( - AboutNode=self.node(config.AboutNodeName), - SensorNode=node, - TelemetryName=config.TelemetryName, - ) - for config in node.component.config_list - ] + TelemetryTuple( + AboutNode=ch.about_node, + SensorNode=ch.captured_by_node, + TelemetryName=ch.TelemetryName, + ) + for ch in channels ) return telemetry_tuples From 2ddef4fcc02335c9b1e7d1786c9d78f5b96e536b Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Fri, 4 Oct 2024 12:28:05 -0400 Subject: [PATCH 165/168] ShNode 200 (Alias -> Name, no more Role) --- docs/asls/enums.rst | 27 --- .../json/component-attribute-class-gt.json | 46 ----- docs/asls/json/component-gt.json | 59 ------ docs/asls/json/data-channel.json | 53 ------ docs/asls/json/egauge-io.json | 57 ------ docs/asls/json/egauge-register-config.json | 60 ------ docs/asls/json/electric-meter-cac-gt.json | 86 --------- .../json/electric-meter-component-gt.json | 98 ---------- .../fibaro-smart-implant-component-gt.json | 66 ------- .../gt-sh-booleanactuator-cmd-status.json | 56 ------ docs/asls/json/gt-sh-cli-atn-cmd.json | 50 ----- .../gt-sh-multipurpose-telemetry-status.json | 76 -------- .../json/gt-sh-simple-telemetry-status.json | 71 ------- docs/asls/json/gt-sh-status.json | 92 --------- ...sh-telemetry-from-multipurpose-sensor.json | 72 ------- docs/asls/json/gt-telemetry.json | 50 ----- docs/asls/json/heartbeat-b.json | 84 -------- docs/asls/json/hubitat-component-gt.json | 74 -------- .../json/hubitat-poller-component-gt.json | 52 ----- docs/asls/json/hubitat-tank-component-gt.json | 50 ----- .../asls/json/multipurpose-sensor-cac-gt.json | 81 -------- .../multipurpose-sensor-component-gt.json | 105 ---------- docs/asls/json/power-watts.json | 24 --- docs/asls/json/resistive-heater-cac-gt.json | 60 ------ .../json/resistive-heater-component-gt.json | 58 ------ docs/asls/json/rest-poller-component-gt.json | 21 -- docs/asls/json/sh-actor-class.json | 65 ------- docs/asls/json/sh-node-role.json | 97 ---------- docs/asls/json/snapshot-spaceheat.json | 48 ----- docs/asls/json/spaceheat-make-model.json | 97 ---------- docs/asls/json/spaceheat-node-gt.json | 93 --------- docs/asls/json/spaceheat-telemetry-name.json | 69 ------- docs/asls/json/spaceheat-unit.json | 56 ------ docs/asls/json/ta-data-channels.json | 77 -------- .../asls/json/telemetry-reporting-config.json | 92 --------- .../json/telemetry-snapshot-spaceheat.json | 72 ------- docs/asls/types.rst | 179 ------------------ docs/types/component-attribute-class-gt.rst | 28 --- docs/types/component-gt.rst | 35 ---- docs/types/data-channel-gt.rst | 67 ------- docs/types/egauge-io.rst | 23 --- docs/types/egauge-register-config.rst | 35 ---- docs/types/electric-meter-cac-gt.rst | 47 ----- docs/types/electric-meter-component-gt.rst | 56 ------ .../fibaro-smart-implant-component-gt.rst | 38 ---- docs/types/gt-dispatch-boolean-local.rst | 45 ----- docs/types/gt-dispatch-boolean.rst | 57 ------ docs/types/gt-driver-booleanactuator-cmd.rst | 41 ---- .../gt-sh-booleanactuator-cmd-status.rst | 36 ---- docs/types/gt-sh-cli-atn-cmd.rst | 36 ---- .../gt-sh-multipurpose-telemetry-status.rst | 42 ---- docs/types/gt-sh-simple-telemetry-status.rst | 42 ---- docs/types/gt-sh-status.rst | 61 ------ ...-sh-telemetry-from-multipurpose-sensor.rst | 39 ---- docs/types/gt-telemetry.rst | 34 ---- docs/types/heartbeat-b.rst | 60 ------ docs/types/hubitat-component-gt.rst | 34 ---- docs/types/hubitat-poller-component-gt.rst | 37 ---- docs/types/hubitat-tank-component-gt.rst | 34 ---- docs/types/multipurpose-sensor-cac-gt.rst | 49 ----- .../multipurpose-sensor-component-gt.rst | 45 ----- docs/types/power-watts.rst | 20 -- docs/types/resistive-heater-cac-gt.rst | 42 ---- docs/types/resistive-heater-component-gt.rst | 41 ---- docs/types/rest-poller-component-gt.rst | 17 -- docs/types/snapshot-spaceheat.rst | 36 ---- docs/types/spaceheat-node-gt.rst | 64 ------- docs/types/telemetry-reporting-config.rst | 51 ----- docs/types/telemetry-snapshot-spaceheat.rst | 39 ---- src/gwproto/data_classes/hardware_layout.py | 110 +++-------- src/gwproto/data_classes/sh_node.py | 18 +- src/gwproto/data_classes/telemetry_tuple.py | 2 +- src/gwproto/enums/__init__.py | 2 - src/gwproto/enums/role.py | 84 -------- src/gwproto/types/spaceheat_node_gt.py | 23 +-- tests/config/hardware-layout.json | 100 ++++------ tests/types/test_spaceheat_node_gt.py | 5 +- 77 files changed, 88 insertions(+), 4160 deletions(-) delete mode 100644 docs/asls/enums.rst delete mode 100644 docs/asls/json/component-attribute-class-gt.json delete mode 100644 docs/asls/json/component-gt.json delete mode 100644 docs/asls/json/data-channel.json delete mode 100644 docs/asls/json/egauge-io.json delete mode 100644 docs/asls/json/egauge-register-config.json delete mode 100644 docs/asls/json/electric-meter-cac-gt.json delete mode 100644 docs/asls/json/electric-meter-component-gt.json delete mode 100644 docs/asls/json/fibaro-smart-implant-component-gt.json delete mode 100644 docs/asls/json/gt-sh-booleanactuator-cmd-status.json delete mode 100644 docs/asls/json/gt-sh-cli-atn-cmd.json delete mode 100644 docs/asls/json/gt-sh-multipurpose-telemetry-status.json delete mode 100644 docs/asls/json/gt-sh-simple-telemetry-status.json delete mode 100644 docs/asls/json/gt-sh-status.json delete mode 100644 docs/asls/json/gt-sh-telemetry-from-multipurpose-sensor.json delete mode 100644 docs/asls/json/gt-telemetry.json delete mode 100644 docs/asls/json/heartbeat-b.json delete mode 100644 docs/asls/json/hubitat-component-gt.json delete mode 100644 docs/asls/json/hubitat-poller-component-gt.json delete mode 100644 docs/asls/json/hubitat-tank-component-gt.json delete mode 100644 docs/asls/json/multipurpose-sensor-cac-gt.json delete mode 100644 docs/asls/json/multipurpose-sensor-component-gt.json delete mode 100644 docs/asls/json/power-watts.json delete mode 100644 docs/asls/json/resistive-heater-cac-gt.json delete mode 100644 docs/asls/json/resistive-heater-component-gt.json delete mode 100644 docs/asls/json/rest-poller-component-gt.json delete mode 100644 docs/asls/json/sh-actor-class.json delete mode 100644 docs/asls/json/sh-node-role.json delete mode 100644 docs/asls/json/snapshot-spaceheat.json delete mode 100644 docs/asls/json/spaceheat-make-model.json delete mode 100644 docs/asls/json/spaceheat-node-gt.json delete mode 100644 docs/asls/json/spaceheat-telemetry-name.json delete mode 100644 docs/asls/json/spaceheat-unit.json delete mode 100644 docs/asls/json/ta-data-channels.json delete mode 100644 docs/asls/json/telemetry-reporting-config.json delete mode 100644 docs/asls/json/telemetry-snapshot-spaceheat.json delete mode 100644 docs/asls/types.rst delete mode 100644 docs/types/component-attribute-class-gt.rst delete mode 100644 docs/types/component-gt.rst delete mode 100644 docs/types/data-channel-gt.rst delete mode 100644 docs/types/egauge-io.rst delete mode 100644 docs/types/egauge-register-config.rst delete mode 100644 docs/types/electric-meter-cac-gt.rst delete mode 100644 docs/types/electric-meter-component-gt.rst delete mode 100644 docs/types/fibaro-smart-implant-component-gt.rst delete mode 100644 docs/types/gt-dispatch-boolean-local.rst delete mode 100644 docs/types/gt-dispatch-boolean.rst delete mode 100644 docs/types/gt-driver-booleanactuator-cmd.rst delete mode 100644 docs/types/gt-sh-booleanactuator-cmd-status.rst delete mode 100644 docs/types/gt-sh-cli-atn-cmd.rst delete mode 100644 docs/types/gt-sh-multipurpose-telemetry-status.rst delete mode 100644 docs/types/gt-sh-simple-telemetry-status.rst delete mode 100644 docs/types/gt-sh-status.rst delete mode 100644 docs/types/gt-sh-telemetry-from-multipurpose-sensor.rst delete mode 100644 docs/types/gt-telemetry.rst delete mode 100644 docs/types/heartbeat-b.rst delete mode 100644 docs/types/hubitat-component-gt.rst delete mode 100644 docs/types/hubitat-poller-component-gt.rst delete mode 100644 docs/types/hubitat-tank-component-gt.rst delete mode 100644 docs/types/multipurpose-sensor-cac-gt.rst delete mode 100644 docs/types/multipurpose-sensor-component-gt.rst delete mode 100644 docs/types/power-watts.rst delete mode 100644 docs/types/resistive-heater-cac-gt.rst delete mode 100644 docs/types/resistive-heater-component-gt.rst delete mode 100644 docs/types/rest-poller-component-gt.rst delete mode 100644 docs/types/snapshot-spaceheat.rst delete mode 100644 docs/types/spaceheat-node-gt.rst delete mode 100644 docs/types/telemetry-reporting-config.rst delete mode 100644 docs/types/telemetry-snapshot-spaceheat.rst delete mode 100644 src/gwproto/enums/role.py diff --git a/docs/asls/enums.rst b/docs/asls/enums.rst deleted file mode 100644 index f991c986..00000000 --- a/docs/asls/enums.rst +++ /dev/null @@ -1,27 +0,0 @@ - -Enum Application Shared Language (ASL) Specifications -=============== - -spaceheat.make.model.001 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/spaceheat-make-model.json - -local.comm.interface.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/local-comm-interface.json - -sh.node.role.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/sh-node-role.json - -spaceheat.unit.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/spaceheat-unit.json - -sh.actor.class.001 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/sh-actor-class.json - -spaceheat.telemetry.name.001 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/spaceheat-telemetry-name.json diff --git a/docs/asls/json/component-attribute-class-gt.json b/docs/asls/json/component-attribute-class-gt.json deleted file mode 100644 index 82bcf4e9..00000000 --- a/docs/asls/json/component-attribute-class-gt.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "component.attribute.class.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Component Attribute Class Gt. Authority for the attributes of the component.attribute.class.gt.000 belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data. Generally speaking, a component attribute class is meant to specify WHAT you might order from a plumbing supply store to 'get the same part.' The Component refers to something that will have a specific serial number.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). This identifier is used to associate a make/model with a specific component (i.e. the component will point to its ComponentAttributeClassId).", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Optional Mutable field to include manufacturer's model name. Note that several different models may be given the same spaceheat.make.model enum name.", - "required": false - }, - "TypeName": { - "type": "string", - "value": "component.attribute.class.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "DisplayName": "Axeman Pressurized 150 Gallon Water Tank", - "TypeName": "component.attribute.class.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/component-gt.json b/docs/asls/json/component-gt.json deleted file mode 100644 index 99bd18a5..00000000 --- a/docs/asls/json/component-gt.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Component Gt. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary identifier for components in all GridWorks registries.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for ComponentAttributeClass object articulated by the component.attribute.class.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "This is an optional, mutable field whose use is strongly encouraged. It may include information about HOW the component is used in a hardware layout. It may also include the HwUid for the component.", - "required": false - }, - "HwUid": { - "type": "string", - "description": "Usually this is determined by the inheriting class.", - "required": false - }, - "TypeName": { - "type": "string", - "value": "component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentId": "780788df-9706-4299-b116-304a48838338", - "DisplayName": "Little Orange house Axeman Tank", - "ComponentAttributeClassId": "683c193a-bf83-4491-a294-c0e32865a407", - "TypeName": "component.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/data-channel.json b/docs/asls/json/data-channel.json deleted file mode 100644 index 64e53f2d..00000000 --- a/docs/asls/json/data-channel.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "data.channel", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Data Channel. A data channel is a concept of some collection of readings that share all characteristics other than time.", - "properties": { - "DisplayName": { - "type": "string", - "description": "This display name is the handle for the data channel. It is meant to be set by the person/people who will be analyzing time series data. It is only expected to be unique within the data channels associated to a specific Terminal Asset.", - "required": true - }, - "AboutName": { - "type": "string", - "format": "SpaceheatName", - "title": "About Name", - "description": "The name of the SpaceheatNode whose physical quantities are getting captured.", - "required": true - }, - "CapturedByName": { - "type": "string", - "format": "SpaceheatName", - "title": "", - "description": "The name of the SpaceheatNode that is capturing the physical quantities (which can be AboutName but does not have to be).", - "required": true - }, - "TelemetryName": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "", - "description": "The name of the physical quantity getting measured.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "data.channel", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "SpaceheatName": { - "type": "string", - "description": "Lowercase words separated by periods, where the word characters can be alphanumeric or a hyphen and the first word starts with an alphabet character.", - "example": "store-hot-pipe" - } - } -} diff --git a/docs/asls/json/egauge-io.json b/docs/asls/json/egauge-io.json deleted file mode 100644 index 62c52767..00000000 --- a/docs/asls/json/egauge-io.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "egauge.io", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Used for an eGauge meter's component information in a hardware layout. When the component associated to a PowerMeter ShNode has MakeModel EGAUGE__4030, there is a significant amount of configuration required to specify both what is read from the eGauge (input) and what is then sent up to the SCADA (output). This type handles that information.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/egauge-io.html", - "properties": { - "InputConfig": { - "type": "egauge.register.config.000", - "description": "This is the data available from the modbus csv map provided by eGauge for this component, for example http://egauge14875.egaug.es/6001C/settings.html for a eGauge device with ID 14875", - "required": true - }, - "OutputConfig": { - "type": "telemetry.reporting.config.000", - "description": "This is the data as the Scada proactor expects to consume it from the power meter driver proactor.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "egauge.io", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "InputConfig": { - "Address": 9016, - "Name": "house-panel-power", - "Description": "change in value", - "Type": "f32", - "Denominator": 1, - "Unit": "W", - "TypeName": "egauge.register.config", - "Version": "000" - }, - "OutputConfig": { - "TelemetryNameGtEnumSymbol": "af39eec9", - "AboutNodeName": "house-panel-power", - "ReportOnChange": true, - "SamplePeriodS": 300, - "Exponent": 0, - "UnitGtEnumSymbol": "f459a9c3", - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 3500, - "TypeName": "telemetry.reporting.config", - "Version": "000" - }, - "TypeName": "egauge.io", - "Version": "000" - } -} diff --git a/docs/asls/json/egauge-register-config.json b/docs/asls/json/egauge-register-config.json deleted file mode 100644 index 1daf8260..00000000 --- a/docs/asls/json/egauge-register-config.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "egauge.register.config", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Used to translate eGauge's Modbus Map. This type captures the information provided by eGauge in its modbus csv map, when reading current, power, energy, voltage, frequency etc from an eGauge 4030.", - "properties": { - "Address": { - "type": "integer", - "description": "EGauge's modbus holding address. Note that the EGauge modbus map for holding address 100 will be 30100 - the '+30000' indicates it is a holding address. We use the 4-digit address after the '3'.", - "required": true - }, - "Name": { - "type": "string", - "description": "The name assigned in the EGauge's modbus map. This is configured by the user (see URL)", - "required": true - }, - "Description": { - "type": "string", - "description": "Again, assigned by the EGauge modbus map. Is usually 'change in value'", - "required": true - }, - "Type": { - "type": "string", - "description": "EGauge's numerical data type. Typically our power measurements are f32 ( 32-bit floating-point number). The serial number & firmware are t16 (which work to treat as 16-bit unsigned integer) and timestamps are u32 (32-bit unsigned integer).", - "required": true - }, - "Denominator": { - "type": "integer", - "description": "Some of the modbus registers divide by 3.60E+06 (cumulative energy registers typically). For the power, current, voltage and phase angle the denominator is 1.", - "required": true - }, - "Unit": { - "type": "string", - "description": "The EGauge unit - typically A, Hz, or W.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "egauge.register.config", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "Address": 9016, - "Name": "house-panel-power", - "Description": "change in value", - "Type": "f32", - "Denominator": 1, - "Unit": "W", - "TypeName": "egauge.register.config", - "Version": "000" - } -} diff --git a/docs/asls/json/electric-meter-cac-gt.json b/docs/asls/json/electric-meter-cac-gt.json deleted file mode 100644 index 45250d4a..00000000 --- a/docs/asls/json/electric-meter-cac-gt.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "electric.meter.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Electric Meter ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "MakeModel", - "description": "The brand name identifier for the electric meter (what you would specify in order to buy one).", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: EGauge 4030", - "required": false - }, - "TelemetryNameList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "spaceheat.telemetry.name", - "title": "TelemetryNames read by this power meter", - "required": true - }, - "PollPeriodMs": { - "type": "integer", - "description": "Poll Period refers to the period of time between two readings by the local actor. This is in contrast to Capture Period, which refers to the period between readings that are sent up to the cloud (or otherwise saved for the long-term).", - "required": true - }, - "Interface": { - "type": "string", - "format": "local.comm.interface", - "title": "", - "required": true - }, - "DefaultBaud": { - "type": "integer", - "required": false - }, - "TypeName": { - "type": "string", - "value": "electric.meter.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentAttributeClassId": "739a6e32-bb9c-43bc-a28d-fb61be665522", - "DisplayName": "EGauge 4030", - "InterfaceGtEnumSymbol": "c1e7a955", - "MakeModelGtEnumSymbol": "beb6d3fb", - "PollPeriodMs": 1000, - "TelemetryNameList": ["af39eec9"], - "TypeName": "electric.meter.cac.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "PositiveInteger": { - "type": "string", - "description": "Must be positive when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 1.7 will be interpreted as 1 and is also fine, while 0.5 is interpreted as 0 and will raise an exception.", - "example": "" - } - } -} diff --git a/docs/asls/json/electric-meter-component-gt.json b/docs/asls/json/electric-meter-component-gt.json deleted file mode 100644 index 8d96a728..00000000 --- a/docs/asls/json/electric-meter-component-gt.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "electric.meter.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Electric Meter Components. Designed for specific instances of Electric Meters. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/electric-meters.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of an ElectricMeter, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for ElectricMeterCac object articulated by the electric.meter.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Oak EGauge6074", - "required": false - }, - "ConfigList": { - "type": "array", - "items": { - "type": "telemetry.reporting.config.000" - }, - "description": "This power meter will produce multiple data channels. Each data channel measures a certain quantities (like power, current) for certain ShNodes (like a boost element or heat pump).", - "required": true - }, - "HwUid": { - "type": "string", - "description": "For eGauge, use what comes back over modbus address 100.", - "required": false - }, - "ModbusHost": { - "type": "string", - "required": false - }, - "ModbusPort": { - "type": "integer", - "format": "NonNegativeInteger", - "title": "", - "required": false - }, - "EgaugeIoList": { - "type": "array", - "items": { - "type": "egauge.io.000" - }, - "description": "This should be empty unless the MakeModel of the corresponding component attribute class is EGauge 4030. The channels that can be read from an EGauge 4030 are configurable by the person who installs the device. The information is encapsulated in a modbus map provided by eGauge as a csv from a device-specific API. The EGaugeIoList maps the data from this map to the data that the SCADA expects to see.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "electric.meter.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "Modbus consistency", - "description": "ModbusHost is None if and only if ModbusPort is None" - }, - "Axiom2": { - "title": "Egauge4030 consistency", - "description": "If the EgaugeIoList has non-zero length, then the ModbusHost is not None and the set of output configs is equal to ConfigList as a set" - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "PositiveInteger": { - "type": "string", - "description": "Must be positive when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 1.7 will be interpreted as 1 and is also fine, while 0.5 is interpreted as 0 and will raise an exception.", - "example": "" - }, - "NonNegativeInteger": { - "type": "string", - "description": "Must be non-negative when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 0 is fine, and 1.7 will be interpreted as 1 and is also fine.", - "example": "" - } - } -} diff --git a/docs/asls/json/fibaro-smart-implant-component-gt.json b/docs/asls/json/fibaro-smart-implant-component-gt.json deleted file mode 100644 index d3c41789..00000000 --- a/docs/asls/json/fibaro-smart-implant-component-gt.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "fibaro.smart.implant.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Fibaro Smart Implant. Designed for specific Fibaro Smart Implants. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://www.fibaro.com/us/products/smart-implant/", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of an Fibaro, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for FibaroSmartImplantCac object articulated by the fibaro.smart.implant.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "ZWaveDSK": { - "type": "string", - "description": "The Z-Wave DSK (Device Specific Key) is a unique identifier associated with a Z-Wave device, used during the process of securely including the device into a Z-Wave network. It helps establish secure communication between the Z-Wave controller and the device, ensuring that only authorized devices can join the network. Unfortunately Hubitat does not currently provide a way to view the ZWave DSK of a Fibaro.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Fibaro Smart Implant 1010 A (For Fibaro A as opposed to B for GridWorks TankModule1 with Serial Number 1010).", - "required": false - }, - "HwUid": { - "type": "string", - "description": "Use the Fibaro S2 PIN Code, which is printed on the back of each Fibaro Implant.", - "required": false - }, - "TypeName": { - "type": "string", - "value": "fibaro.smart.implant.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ComponentId": "1fdd40dd-14d7-4da2-8cf8-7cf66484e385", - "ComponentAttributeClassId": "7ce0ce69-14c6-4cb7-a33f-2aeca91e0680", - "DisplayName": "Fibaro 1010 A", - "ZWaveDSK": "", - "HwUid": "20134", - "TypeName": "fibaro.smart.implant.component.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/gt-sh-booleanactuator-cmd-status.json b/docs/asls/json/gt-sh-booleanactuator-cmd-status.json deleted file mode 100644 index 353ac550..00000000 --- a/docs/asls/json/gt-sh-booleanactuator-cmd-status.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.booleanactuator.cmd.status", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Boolean Actuator Driver Command Status Package. This is a subtype of the status message sent from a SCADA to its AtomicTNode. It contains a list of all the commands that a particular boolean actuator actor has reported as sending as actuation commands to its driver in the last transmission period (typically 5 minutes).", - "url": "https://gridworks.readthedocs.io/en/latest/relay-state.html", - "properties": { - "ShNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "SpaceheatNodeAlias", - "description": "The alias of the spaceheat node that is getting actuated. For example, `a.elt1.relay` would likely indicate the relay for a resistive element.", - "required": true - }, - "RelayStateCommandList": { - "type": "array", - "items": { - "type": "integer" - }, - "required": true - }, - "CommandTimeUnixMsList": { - "type": "array", - "items": { - "type": "integer" - }, - "format": "ReasonableUnixTimeMs", - "title": "List of Command Times", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.booleanactuator.cmd.status", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "formats": { - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-sh-cli-atn-cmd.json b/docs/asls/json/gt-sh-cli-atn-cmd.json deleted file mode 100644 index 553a543e..00000000 --- a/docs/asls/json/gt-sh-cli-atn-cmd.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.cli.atn.cmd", - "version": "110", - "owner": "gridworks@gridworks-consulting.com", - "description": "AtomicTNode CLI Command. This is a generic type mechanism for a crude command line interface on a SCADA, brokered by the AtomicTNode.", - "properties": { - "FromGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "GNodeAlias", - "description": "Must be the SCADA's AtomicTNode.", - "required": true - }, - "SendSnapshot": { - "type": "boolean", - "description": "Asks SCADA to send back a snapshot. For this version of the type, nothing would happen if SendSnapshot were set to False. However, we include this in case additional variations are added later.", - "required": true - }, - "FromGNodeId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "GNodeId", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.cli.atn.cmd", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "110", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/gt-sh-multipurpose-telemetry-status.json b/docs/asls/json/gt-sh-multipurpose-telemetry-status.json deleted file mode 100644 index dab671b3..00000000 --- a/docs/asls/json/gt-sh-multipurpose-telemetry-status.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.multipurpose.telemetry.status", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Data read from a MultipurposeSensor run by a Spaceheat SCADA. A list of readings about a specific SpaceheatNode made by a MultipurposeSensor node, for a Spaceheat SCADA. Designed as part of a status message sent from the SCADA to its AtomicTNode typically once every 5 minutes. The nth element of each of its two lists refer to the same reading (i.e. what the value is, when it was read).", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html", - "properties": { - "AboutNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "AboutNodeAlias", - "description": "The SpaceheatNode representing the physical object that the sensor reading is collecting data about. For example, a multipurpose temp sensor that reads 12 temperatures would have data for 12 different AboutNodeAliases, including say `a.tank1.temp1` for a temp sensor at the top of a water tank.", - "required": true - }, - "SensorNodeAlias": { - "type": "string", - "description": "The alias of the SpaceheatNode representing the telemetry device", - "required": true - }, - "TelemetryName": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "TelemetryName", - "description": "The TelemetryName of the readings. This is used to interpet the meaning of the reading values. For example, WaterTempCTimes1000 means the reading is measuring the a reading of 37 deg C.", - "required": true - }, - "ValueList": { - "type": "array", - "items": { - "type": "integer" - }, - "description": "The values of the readings.", - "required": true - }, - "ReadTimeUnixMsList": { - "type": "array", - "items": { - "type": "integer" - }, - "format": "ReasonableUnixTimeMs", - "title": "List of Read Times", - "description": "The times that the MultipurposeSensor took the readings, in unix milliseconds", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.multipurpose.telemetry.status", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "ListLengthConsistency", - "description": "ValueList and ReadTimeUnixMsList must have the same length." - } - }, - "formats": { - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-sh-simple-telemetry-status.json b/docs/asls/json/gt-sh-simple-telemetry-status.json deleted file mode 100644 index bcbbd73d..00000000 --- a/docs/asls/json/gt-sh-simple-telemetry-status.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.simple.telemetry.status", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Data read from a SimpleSensor run by a SpaceHeat SCADA. A list of readings from a simple sensor for a Spaceheat SCADA. Designed as part of a status message sent from the SCADA to its AtomicTNode typically once every 5 minutes. The nth element of each of its two lists refer to the same reading (i.e. what the value is, when it was read).", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/simple-sensor.html", - "properties": { - "ShNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "SpaceheatNodeAlias", - "description": "The Alias of the SimpleSensor associated to the readings", - "required": true - }, - "TelemetryName": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "TelemetryName", - "description": "The TelemetryName of the readings. This is used to interpet the meaning of the reading values. For example, WaterTempCTimes1000 means the reading is measuring the temperature of water, in Celsius multiplied by 1000. So a value of 37000 would be a reading of 37 deg C.", - "required": true - }, - "ValueList": { - "type": "array", - "items": { - "type": "integer" - }, - "description": "The values of the readings.", - "required": true - }, - "ReadTimeUnixMsList": { - "type": "array", - "items": { - "type": "integer" - }, - "format": "ReasonableUnixTimeMs", - "title": "List of Read Times", - "description": "The times that the SImpleSensor took the readings, in unix milliseconds", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.simple.telemetry.status", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "ListLengthConsistency", - "description": "ValueList and ReadTimeUnixMsList must have the same length." - } - }, - "formats": { - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-sh-status.json b/docs/asls/json/gt-sh-status.json deleted file mode 100644 index c851d931..00000000 --- a/docs/asls/json/gt-sh-status.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.status", - "version": "110", - "owner": "gridworks@gridworks-consulting.com", - "description": ". Status message sent by a Spaceheat SCADA every 5 minutes", - "properties": { - "FromGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "required": true - }, - "FromGNodeId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "", - "required": true - }, - "AboutGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "required": true - }, - "SlotStartUnixS": { - "type": "integer", - "format": "ReasonableUnixTimeS", - "title": "", - "required": true - }, - "ReportingPeriodS": { - "type": "integer", - "required": true - }, - "SimpleTelemetryList": { - "type": "array", - "items": { - "type": "gt.sh.simple.telemetry.status.100" - }, - "required": true - }, - "MultipurposeTelemetryList": { - "type": "array", - "items": { - "type": "gt.sh.multipurpose.telemetry.status.100" - }, - "required": true - }, - "BooleanactuatorCmdList": { - "type": "array", - "items": { - "type": "gt.sh.booleanactuator.cmd.status.100" - }, - "required": true - }, - "StatusUid": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.status", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "110", - "required": true - } - }, - "formats": { - "ReasonableUnixTimeS": { - "type": "string", - "description": "Integer reflecting unix time seconds between 1970 and 3000", - "example": "" - }, - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/gt-sh-telemetry-from-multipurpose-sensor.json b/docs/asls/json/gt-sh-telemetry-from-multipurpose-sensor.json deleted file mode 100644 index 1de6b57e..00000000 --- a/docs/asls/json/gt-sh-telemetry-from-multipurpose-sensor.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.sh.telemetry.from.multipurpose.sensor", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Data sent from a MultipurposeSensor to a Spaceheat SCADA. A set of readings made at the same time by a multipurpose sensor, sent by the MultipurposeSensor SpaceheatNode actor to its SCADA. The nth element of each of its three readings (what is getting read, what the value is, what the TelemetryNames are).", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html", - "properties": { - "ScadaReadTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "ScadaReadTime in Unix MilliSeconds", - "required": true - }, - "AboutNodeAliasList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "LeftRightDot", - "title": "AboutNodeAliasList", - "description": "List of aliases of the SpaceHeat Nodes getting measured", - "required": true - }, - "TelemetryNameList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "spaceheat.telemetry.name", - "title": "TelemetryNameList", - "description": "List of the TelemetryNames. The nth name in this list indicates the TelemetryName of the nth alias in the AboutNodeAliasList.", - "required": true - }, - "ValueList": { - "type": "array", - "items": { - "type": "integer" - }, - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.sh.telemetry.from.multipurpose.sensor", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "ListLengthConsistency", - "description": "AboutNodeAliasList, ValueList and TelemetryNameList must all have the same length." - } - }, - "formats": { - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/gt-telemetry.json b/docs/asls/json/gt-telemetry.json deleted file mode 100644 index dcb3b390..00000000 --- a/docs/asls/json/gt-telemetry.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "gt.telemetry", - "version": "110", - "owner": "gridworks@gridworks-consulting.com", - "description": "Data sent from a SimpleSensor to a SCADA. This type is meant to be used by a SimpleSensor, where _what_ is doing the reading can be conflated with _what_ is being read.", - "properties": { - "ScadaReadTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "Scada Read Time in Unix Milliseconds", - "required": true - }, - "Value": { - "type": "integer", - "description": "The value of the reading.", - "required": true - }, - "Name": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "Name", - "description": "The name of the Simple Sensing Spaceheat Node. This is both the AboutNodeName and FromNodeName for a data channel. The TelemetryName (and thus Units) are expected to be inferred by the Spaceheat Node. For example this is done initially in SCADA code according to whether the component of the Node is a PipeFlowSensorComponent, SimpleTempSensorComponent etc.", - "required": true - }, - "Exponent": { - "type": "integer", - "description": "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C", - "required": true - }, - "TypeName": { - "type": "string", - "value": "gt.telemetry", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "110", - "required": true - } - }, - "formats": { - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/heartbeat-b.json b/docs/asls/json/heartbeat-b.json deleted file mode 100644 index cc6ddf8c..00000000 --- a/docs/asls/json/heartbeat-b.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "heartbeat.b", - "version": "001", - "owner": "gridworks@gridworks-consulting.com", - "description": "Heartbeat B. This is the Heartbeat intended to be sent between the Scada and the AtomicTNode to allow for block-chain validation of the status of their communication.", - "url": "https://gridworks.readthedocs.io/en/latest/dispatch-contract.html", - "properties": { - "FromGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "My GNodeAlias", - "required": true - }, - "FromGNodeInstanceId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "My GNodeInstanceId", - "required": true - }, - "MyHex": { - "type": "string", - "format": "HexChar", - "title": "Hex character getting sent", - "required": true - }, - "YourLastHex": { - "type": "string", - "format": "HexChar", - "title": "Last hex character received from heartbeat partner.", - "required": true - }, - "LastReceivedTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "Time YourLastHex was received on my clock", - "required": true - }, - "SendTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "Time this message is made and sent on my clock", - "required": true - }, - "StartingOver": { - "type": "boolean", - "description": "(typically the AtomicTNode in an AtomicTNode / SCADA pair) wants to start the heartbeating volley over. The result is that its partner will not expect the initiator to know its last Hex.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "heartbeat.b", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "001", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "HexChar": { - "type": "string", - "description": "single-char string in '0123456789abcdefABCDEF'", - "example": "d" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/json/hubitat-component-gt.json b/docs/asls/json/hubitat-component-gt.json deleted file mode 100644 index ea3ff022..00000000 --- a/docs/asls/json/hubitat-component-gt.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Component (GridWorks Type). Designed for specific Hubitat hubs. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/iot-hubs.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a Hubitat, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for HubitatCac object articulated by the hubitat.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "Hubitat": { - "type": "dictDict", - "description": "Includes the information needed to access the MakerAPI of a Hubitat on the Local area network: Host, MakerApiID, AccessToken and MacAddress for the Hubitat.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Oak Hubitat 81:37:82 (using the last 6 digits of the Hubitat MacId in the display name, as well as the short alias for the associated g node.)", - "required": false - }, - "HwUid": { - "type": "string", - "description": "Use the final 6 characters of the Hubitat mac address.", - "required": false - }, - "TypeName": { - "type": "string", - "value": "hubitat.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "Hubitat.MacAddressId must have MacAddress format", - "description": "Mac Address format is 6 pairs of double hex digits separated by colons, agnostic to caps. - e.g. '34:E1:D1:81:37:82' and '34:e1:d1:81:37:82' both satisfy this property. This works in python: MAC_REGEX = re.compile('[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$') and then bool(MAC_REGEX.match(mac_str.lower()))" - }, - "Axiom2": { - "title": "HwUid matches Hubitat MacAddress", - "description": "The HwUid must exist, and it must be lower alphanumeric versions of the last 6 digits of the Hubitat.MacAddressId with the colons taken out. For example, if the HubitatMacAddressId is '34:E1:D1:81:37:8A' then the HwUid must be '81378a'." - } - }, - "example": { - "ComponentAttributeClassId": "62528da5-b510-4ac2-82c1-3782842eae07", - "ComponentId": "48039704-7d45-4937-adda-0e362d13cef6", - "DisplayName": "Oak Hubitat 81:37:82", - "Hubitat": { - "AccessToken": "a8232144-abe9-4eed-bcfd-8f182600b8e7", - "Host": "hubitat-keene-oak.local", - "MacAddress": "34:E1:D1:81:37:82", - "MakerApiId": 2 - }, - "HwUid": "813782", - "TypeName": "hubitat.component.gt", - "Version": "000" - } -} diff --git a/docs/asls/json/hubitat-poller-component-gt.json b/docs/asls/json/hubitat-poller-component-gt.json deleted file mode 100644 index beae3574..00000000 --- a/docs/asls/json/hubitat-poller-component-gt.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.poller.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Poller Component (GridWorks Types). A specific instance of a Hubitat Poller Cac (like a Honeywell T6 Thermostat) - a device that can be polled through a Hubitat IoT hub.", - "properties": { - "ComponentId": { - "type": "string", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for HubitatPollerCac object articulated by the hubitat.poller.cac.gt.000 type.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: Downstairs Thermostat", - "required": false - }, - "HwUid": { - "type": "string", - "description": "Unique Hardware Identifier", - "required": false - }, - "Poller": { - "type": "dictDict", - "description": "Includes hubitat_component_id (str), device_id (int), enabled (bool), poll_period_s (int) and attributes.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "hubitat.poller.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/hubitat-tank-component-gt.json b/docs/asls/json/hubitat-tank-component-gt.json deleted file mode 100644 index 9dcb6c24..00000000 --- a/docs/asls/json/hubitat-tank-component-gt.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "hubitat.tank.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Hubitat Tank Component (GridWorks Type). This is a specific instance of a GridWorks temp sensing Tank Module that uses a Hubitat to read the remote data. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/gridworks-tank-module-1.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a GridWorks TankModule1 and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for HubitatTankCac object articulated by the hubitat.tank.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "Tank": { - "type": "dictDict", - "description": "The configuration information (HubitatTankSettingsGt) about the 4 analog temperature sensors for a GridWorks TankModule1.", - "required": true - }, - "DisplayName": { - "type": "string", - "description": "Sample: GridWorks TankModule SN 1010", - "required": false - }, - "HwUid": { - "type": "string", - "description": "Use the GridWorks Serial number for GridWorks TankModule1.", - "required": false - }, - "TypeName": { - "type": "string", - "value": "hubitat.tank.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - } -} diff --git a/docs/asls/json/multipurpose-sensor-cac-gt.json b/docs/asls/json/multipurpose-sensor-cac-gt.json deleted file mode 100644 index 017cc484..00000000 --- a/docs/asls/json/multipurpose-sensor-cac-gt.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "multipurpose.sensor.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Multipuprose Sensor ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "MakeModel", - "description": "Meant to be enough to articulate any difference in how GridWorks code would interact with a device. Should be able to use this information to buy or build a device.", - "required": true - }, - "PollPeriodMs": { - "type": "integer", - "description": "Poll Period refers to the period of time between two readings by the local actor. This is in contrast to Capture Period, which refers to the period between readings that are sent up to the cloud (or otherwise saved for the long-term).", - "required": true - }, - "Exponent": { - "type": "integer", - "description": "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C", - "required": true - }, - "TempUnit": { - "type": "string", - "format": "spaceheat.unit", - "title": "Temp Unit", - "required": true - }, - "TelemetryNameList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "spaceheat.telemetry.name", - "title": "", - "required": true - }, - "MaxThermistors": { - "type": "integer", - "description": "The maximum number of temperature sensors this multipurpose sensor can read.", - "required": false - }, - "DisplayName": { - "type": "string", - "description": "Sample: GridWorks TSnap1.0 as 12-channel analog temp sensor", - "required": false - }, - "CommsMethod": { - "type": "string", - "required": false - }, - "TypeName": { - "type": "string", - "value": "multipurpose.sensor.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/multipurpose-sensor-component-gt.json b/docs/asls/json/multipurpose-sensor-component-gt.json deleted file mode 100644 index 6b87af0e..00000000 --- a/docs/asls/json/multipurpose-sensor-component-gt.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "multipurpose.sensor.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Multupurpose Sensor Components. This type was first designed to work with a 12-channel analog temp sensor built into the first generation GridWorks SCADA box (GSCADA 1). It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a MultipurposeSensor (perhaps only the 12-channel analog temp sensor), and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for MultipurposeSensorCac object articulated by the multipurpose.sensor.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "ChannelList": { - "type": "array", - "items": { - "type": "integer" - }, - "required": true - }, - "ConfigList": { - "type": "array", - "items": { - "type": "telemetry.reporting.config.000" - }, - "required": true - }, - "HwUid": { - "type": "string", - "required": false - }, - "DisplayName": { - "type": "string", - "description": "Sample: Oak Multipurpose Temp Sensor Component <100>", - "required": false - }, - "TypeName": { - "type": "string", - "value": "multipurpose.sensor.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "example": { - "ChannelList": [1, 2], - "ComponentAttributeClassId": "432073b8-4d2b-4e36-9229-73893f33f846", - "ComponentId": "109e0bde-2f04-4cd4-9e69-bb2732a368e2", - "ConfigList": [ - { - "AboutNodeName": "a.dist.swt.temp", - "AsyncReportThreshold": 0.005, - "Exponent": 3, - "NameplateMaxValue": 100000, - "ReportOnChange": true, - "SamplePeriodS": 60, - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "ec14bd47", - "Version": "000" - }, - { - "AboutNodeName": "a.dist.rwt.temp", - "AsyncReportThreshold": 0.005, - "Exponent": 3, - "NameplateMaxValue": 100000, - "ReportOnChange": true, - "SamplePeriodS": 60, - "TelemetryNameGtEnumSymbol": "c89d0ba1", - "TypeName": "telemetry.reporting.config", - "UnitGtEnumSymbol": "ec14bd47", - "Version": "000" - } - ], - "DisplayName": "Multipurpose Temp Sensor Component <100>", - "HwUid": "100", - "TypeName": "multipurpose.sensor.component.gt", - "Version": "000" - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/power-watts.json b/docs/asls/json/power-watts.json deleted file mode 100644 index c2677912..00000000 --- a/docs/asls/json/power-watts.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "power.watts", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Real-time power of TerminalAsset in Watts. Used by a SCADA -> Atn or Atn -> AggregatedTNode to report real-time power of their TerminalAsset. Positive number means WITHDRAWAL from the grid - so generating electricity creates a negative number. This message is considered worse than useless to send after the first attempt, and does not require an ack. Shares the same purpose as gs.pwr, but is not designed to minimize bytes so comes in JSON format.", - "properties": { - "Watts": { - "type": "integer", - "required": true - }, - "TypeName": { - "type": "string", - "value": "power.watts", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - } -} diff --git a/docs/asls/json/resistive-heater-cac-gt.json b/docs/asls/json/resistive-heater-cac-gt.json deleted file mode 100644 index df7a599b..00000000 --- a/docs/asls/json/resistive-heater-cac-gt.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "resistive.heater.cac.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Resistive Heater ComponentAttributeClasses. GridWorks Spaceheat SCADA uses the GridWorks GNodeRegistry structures and abstractions for managing relational device data. The Cac, or ComponentAttributeClass, is part of this structure.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component-attribute-class.html", - "properties": { - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "ComponentAttributeClassId", - "description": "Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry.", - "required": true - }, - "MakeModel": { - "type": "string", - "format": "spaceheat.make.model", - "title": "", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "NameplateMaxPowerW": { - "type": "integer", - "required": true - }, - "RatedVoltageV": { - "type": "integer", - "format": "PositiveInteger", - "title": "", - "required": true - }, - "TypeName": { - "type": "string", - "value": "resistive.heater.cac.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "PositiveInteger": { - "type": "string", - "description": "Must be positive when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 1.7 will be interpreted as 1 and is also fine, while 0.5 is interpreted as 0 and will raise an exception.", - "example": "" - } - } -} diff --git a/docs/asls/json/resistive-heater-component-gt.json b/docs/asls/json/resistive-heater-component-gt.json deleted file mode 100644 index a2033e06..00000000 --- a/docs/asls/json/resistive-heater-component-gt.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "resistive.heater.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Type for tracking Resistive Heater Components. Designed for Resistive Heaters. It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component.html", - "properties": { - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Component Id", - "description": "Primary GridWorks identifier for a specific physical instance of a ResistiveHeater, and also as a more generic Component.", - "required": true - }, - "ComponentAttributeClassId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for ResistiveHeaterCac object articulated by the resistive.heater.cac.gt.000 type.", - "description": "Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry.", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "HwUid": { - "type": "string", - "required": false - }, - "TestedMaxHotMilliOhms": { - "type": "integer", - "required": false - }, - "TestedMaxColdMilliOhms": { - "type": "integer", - "required": false - }, - "TypeName": { - "type": "string", - "value": "resistive.heater.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - } - } -} diff --git a/docs/asls/json/rest-poller-component-gt.json b/docs/asls/json/rest-poller-component-gt.json deleted file mode 100644 index 7abe0aea..00000000 --- a/docs/asls/json/rest-poller-component-gt.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "rest.poller.component.gt", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "REST Poller Component (GridWorks Type). Designed for generic REST Pollers (like Honeywell Z-Wave thermostats). It extends the component.gt.000 type. Authority for the attributes of the component.gt.000 (ComponentId, ComponentAttributeClassId, DisplayName, HwUid) belongs to the WorldRegistry. The WorldRegistry is part of the GridWorks 'BackOffice' structure for managing relational device data . Notably, ComponentId and ComponentAttributeClass are both required and immutable. HwUid is optional but once it is set to a non-null value that is also immutable - it is meant to be an immutable identifier associated to a specific physical device, ideally one that can be read remotely by the SCADA and also by the naked eye. The DisplayName is mutable, with its current value in time governed by the WorldRegistry.", - "url": "https://g-node-registry.readthedocs.io/en/latest/component.html", - "properties": { - "TypeName": { - "type": "string", - "value": "rest.poller.component.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - } -} diff --git a/docs/asls/json/sh-actor-class.json b/docs/asls/json/sh-actor-class.json deleted file mode 100644 index ac165327..00000000 --- a/docs/asls/json/sh-actor-class.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "sh.actor.class", - "enum_version": "001", - "description": "Determines the code running Spaceheat Nodes supervised by Spaceheat SCADA software", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/actor-class.html", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shactorclass", - "values": [ - "NoActor", - "Scada", - "HomeAlone", - "BooleanActuator", - "PowerMeter", - "Atn", - "SimpleSensor", - "MultipurposeSensor", - "Thermostat", - "HubitatTelemetryReader", - "HubitatTankModule", - "HubitatPoller" - ], - "value_to_gt_symbol": { - "NoActor": "00000000", - "Scada": "6d37aa41", - "HomeAlone": "32d3d19f", - "BooleanActuator": "fddd0064", - "PowerMeter": "2ea112b9", - "Atn": "b103058f", - "SimpleSensor": "dae4b2f0", - "MultipurposeSensor": "7c483ad0", - "Thermostat": "4a9c1785", - "HubitatTelemetryReader": "0401b27e", - "HubitatTankModule": "e2877329", - "HubitatPoller": "00000100" - }, - "value_to_version": { - "NoActor": "000", - "Scada": "000", - "HomeAlone": "000", - "BooleanActuator": "000", - "PowerMeter": "000", - "Atn": "000", - "SimpleSensor": "000", - "MultipurposeSensor": "000", - "Thermostat": "000", - "HubitatTelemetryReader": "001", - "HubitatTankModule": "001", - "HubitatPoller": "001" - }, - "value_descriptions": { - "NoActor": "A SpaceheatNode that does not have any code running on its behalf within the SCADA, but is instead only a reference object (for example, a tank of hot water or a resistive element) that can be discussed (for example, the power drawn by the resistive element can be measured) or evaluated (for example, a set of 5 different temperatures in different places on the tank can be used to estimate total thermal energy in the tank).", - "Scada": "The SCADA actor is the prime piece of code running and supervising other ProActors within the SCADA code. It is also responsible for managing the state of TalkingWith the AtomicTNode, as well maintaining and reporting a boolean state variable that indicates whether it is following dispatch commands from the AtomicTNode XOR following dispatch commands from its own HomeAlone actor.", - "HomeAlone": "HomeAlone is an abstract Spaceheat Actor responsible for dispatching the SCADA when it is not talking with the AtomicTNode.", - "BooleanActuator": "A SpaceheatNode representing a generic boolean actuator capable of turning on (closing a circuit) or turning off (opening a circuit).", - "PowerMeter": "A SpaceheatNode representing the power meter that is used to settle financial transactions with the TerminalAsset. That is, this is the power meter whose accuracy is certified in the creation of the TerminalAsset GNode via creation of the TaDeed. More Info: https://gridworks.readthedocs.io/en/latest/terminal-asset.html", - "Atn": "A SpaceheatNode representing the AtomicTNode. Note that the code running the AtomicTNode is not local within the SCADA code, except for a stub used for testing purposes. More Info: https://gridworks.readthedocs.io/en/latest/atomic-t-node.html", - "SimpleSensor": "A SpaceheatNode representing a sensor that measures a single category of quantity (for example, temperature) for a single object (for example, on a pipe). More Info: https://gridworks-protocol.readthedocs.io/en/latest/simple-sensor.html", - "MultipurposeSensor": "A sensor that either reads multiple kinds of readings from the same sensing device (for example reads current and voltage), reads multiple different objects (temperature from two different thermisters) or both. More Info: https://gridworks-protocol.readthedocs.io/en/latest/multipurpose-sensor.html", - "Thermostat": "A SpaceheatNode representing a thermostat.", - "HubitatTelemetryReader": "A generic actor for reading telemetry data from a Hubitat Home Automation Hub LAN API. More Info: https://drive.google.com/drive/u/0/folders/1AqAU_lC2phzuI9XRYvogiIYA7GXNtlr6", - "HubitatTankModule": "The actor for running a GridWorks TankModule, comprised of two Z-Wave Fibaro temp sensors built together inside a small container that has 4 thermistors attached. These are designed to be installed from top (1) to bottom (4) on a stratified thermal storage tank. More Info: https://drive.google.com/drive/u/0/folders/1GSxDd8Naf1GKK_fSOgQU933M1UcJ4r8q", - "HubitatPoller": "An actor for representing a somewhat generic ShNode (like a thermostat) that can be polled through the Hubitat." - }, - "default_value": "NoActor" -} diff --git a/docs/asls/json/sh-node-role.json b/docs/asls/json/sh-node-role.json deleted file mode 100644 index 48d6acfd..00000000 --- a/docs/asls/json/sh-node-role.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "sh.node.role", - "enum_version": "000", - "description": "Categorizes SpaceheatNodes by their function within the heating system", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node-role.html", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole", - "values": [ - "Unknown", - "Scada", - "HomeAlone", - "Atn", - "PowerMeter", - "BoostElement", - "BooleanActuator", - "DedicatedThermalStore", - "TankWaterTempSensor", - "PipeTempSensor", - "RoomTempSensor", - "OutdoorTempSensor", - "PipeFlowMeter", - "HeatedSpace", - "HydronicPipe", - "BaseboardRadiator", - "RadiatorFan", - "CirculatorPump", - "MultiChannelAnalogTempSensor", - "Outdoors" - ], - "value_to_gt_symbol": { - "Unknown": "00000000", - "Scada": "d0afb424", - "HomeAlone": "863e50d1", - "Atn": "6ddff83b", - "PowerMeter": "9ac68b6e", - "BoostElement": "99c5f326", - "BooleanActuator": "57b788ee", - "DedicatedThermalStore": "3ecfe9b8", - "TankWaterTempSensor": "73308a1f", - "PipeTempSensor": "c480f612", - "RoomTempSensor": "fec74958", - "OutdoorTempSensor": "5938bf1f", - "PipeFlowMeter": "ece3b600", - "HeatedSpace": "65725f44", - "HydronicPipe": "fe3cbdd5", - "BaseboardRadiator": "05fdd645", - "RadiatorFan": "6896109b", - "CirculatorPump": "b0eaf2ba", - "MultiChannelAnalogTempSensor": "661d7e73", - "Outdoors": "dd975b31" - }, - "value_to_version": { - "Unknown": "000", - "Scada": "000", - "HomeAlone": "000", - "Atn": "000", - "PowerMeter": "000", - "BoostElement": "000", - "BooleanActuator": "000", - "DedicatedThermalStore": "000", - "TankWaterTempSensor": "000", - "PipeTempSensor": "000", - "RoomTempSensor": "000", - "OutdoorTempSensor": "000", - "PipeFlowMeter": "000", - "HeatedSpace": "000", - "HydronicPipe": "000", - "BaseboardRadiator": "000", - "RadiatorFan": "000", - "CirculatorPump": "000", - "MultiChannelAnalogTempSensor": "000", - "Outdoors": "000" - }, - "value_descriptions": { - "Unknown": "Unknown Role", - "Scada": "Primary SCADA", - "HomeAlone": "HomeAlone GNode", - "Atn": "AtomicTNode", - "PowerMeter": "A SpaceheatNode representing the power meter that is used to settle financial transactions with the TerminalAsset. That is, this is the power meter whose accuracy is certified in the creation of the TerminalAsset GNode via creation of the TaDeed. More Info: https://gridworks.readthedocs.io/en/latest/terminal-asset.html", - "BoostElement": "Resistive element used for providing heat to a thermal store.", - "BooleanActuator": "A solid state or mechanical relay with two states (open, closed)", - "DedicatedThermalStore": "A dedicated thermal store within a thermal storage heating system - could be one or more water tanks, phase change material, etc.", - "TankWaterTempSensor": "A temperature sensor used for measuring temperature inside or on the immediate outside of a water tank.", - "PipeTempSensor": "A temperature sensor used for measuring the temperature of a tank. Typically curved metal thermistor with thermal grease for good contact.", - "RoomTempSensor": "A temperature sensor used for measuring room temperature, or temp in a heated space more generally.", - "OutdoorTempSensor": "A temperature sensor used for measuring outdoor temperature.", - "PipeFlowMeter": "A meter that measures flow of liquid through a pipe, in units of VOLUME/TIME", - "HeatedSpace": "A Heated Space.", - "HydronicPipe": "A pipe carrying techinical water or other fluid (e.g. glycol) in a heating system.", - "BaseboardRadiator": "A baseboard radiator - one kind of emitter in a hydronic heating system.", - "RadiatorFan": "A fan that can amplify the power out of a radiator.", - "CirculatorPump": "Circulator pump for one or more of the hydronic pipe loops", - "MultiChannelAnalogTempSensor": "An analog multi channel temperature sensor", - "Outdoors": "The outdoors" - }, - "default_value": "Unknown" -} diff --git a/docs/asls/json/snapshot-spaceheat.json b/docs/asls/json/snapshot-spaceheat.json deleted file mode 100644 index 748403fe..00000000 --- a/docs/asls/json/snapshot-spaceheat.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "snapshot.spaceheat", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "", - "properties": { - "FromGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "required": true - }, - "FromGNodeInstanceId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "", - "required": true - }, - "Snapshot": { - "type": "telemetry.snapshot.spaceheat.000", - "required": true - }, - "TypeName": { - "type": "string", - "value": "snapshot.spaceheat", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/spaceheat-make-model.json b/docs/asls/json/spaceheat-make-model.json deleted file mode 100644 index fbfd5730..00000000 --- a/docs/asls/json/spaceheat-make-model.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "spaceheat.make.model", - "enum_version": "001", - "description": "Determines Make/Model of device associated to a Spaceheat Node supervised by SCADA", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/make-model.html", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel", - "values": [ - "UnknownMake__UnknownModel", - "Egauge__4030", - "NCD__PR8-14-SPST", - "Adafruit__642", - "GridWorks__TSnap1", - "GridWorks__WaterTempHighPrecision", - "Gridworks__SimPm1", - "SchneiderElectric__Iem3455", - "GridWorks__SimBool30AmpRelay", - "OpenEnergy__EmonPi", - "GridWorks__SimTSnap1", - "Atlas__EzFlo", - "Hubitat__C7__LAN1", - "GridWorks__Tank_Module_1", - "Fibaro__Analog_Temp_Sensor", - "Amphenol__NTC_10K_Thermistor_MA100GG103BN", - "YHDC__SCT013-100", - "Magnelab__SCT-0300-050", - "GridWorks__MultiTemp1", - "Krida__Emr16-I2c-V3" - ], - "value_to_gt_symbol": { - "UNKNOWNMAKE__UNKNOWNMODEL": "00000000", - "EGAUGE__4030": "beb6d3fb", - "NCD__PR814SPST": "fabfa505", - "ADAFRUIT__642": "acd93fb3", - "GRIDWORKS__TSNAP1": "d0178dc3", - "GRIDWORKS__WATERTEMPHIGHPRECISION": "f8b497e8", - "GRIDWORKS__SIMPM1": "076da322", - "SCHNEIDERELECTRIC__IEM3455": "d300635e", - "GRIDWORKS__SIMBOOL30AMPRELAY": "e81d74a8", - "OPENENERGY__EMONPI": "c75d269f", - "GRIDWORKS__SIMTSNAP1": "3042c432", - "ATLAS__EZFLO": "d0b0e375", - "HUBITAT__C7__LAN1": "4d649420", - "GRIDWORKS__TANK_MODULE_1": "bd759051", - "FIBARO__ANALOG_TEMP_SENSOR": "1f19839d", - "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN": "46f21cd5", - "YHDC__SCT013100": "08da3f7d", - "MAGNELAB__SCT0300050": "a8d9a70d", - "GRIDWORKS__MULTITEMP1": "bb31d136", - "KRIDA__EMR16I2CV3": "3353ce46" - }, - "value_to_version": { - "UNKNOWNMAKE__UNKNOWNMODEL": "000", - "EGAUGE__4030": "000", - "NCD__PR814SPST": "000", - "ADAFRUIT__642": "000", - "GRIDWORKS__TSNAP1": "000", - "GRIDWORKS__WATERTEMPHIGHPRECISION": "000", - "GRIDWORKS__SIMPM1": "000", - "SCHNEIDERELECTRIC__IEM3455": "000", - "GRIDWORKS__SIMBOOL30AMPRELAY": "000", - "OPENENERGY__EMONPI": "000", - "GRIDWORKS__SIMTSNAP1": "000", - "ATLAS__EZFLO": "000", - "HUBITAT__C7__LAN1": "001", - "GRIDWORKS__TANK_MODULE_1": "001", - "FIBARO__ANALOG_TEMP_SENSOR": "001", - "AMPHENOL__NTC_10K_THERMISTOR_MA100GG103BN": "001", - "YHDC__SCT013100": "001", - "MAGNELAB__SCT0300050": "001", - "GRIDWORKS__MULTITEMP1": "001", - "KRIDA__EMR16I2CV3": "001" - }, - "value_descriptions": { - "UnknownMake__UnknownModel": "", - "Egauge__4030": "A power meter in Egauge's 403x line. More Info: https://drive.google.com/drive/u/0/folders/1abJ-o9tlTscsQpMvT6SHxIm5j5aODgfA", - "NCD__PR8-14-SPST": "NCD's 4-channel high-power relay controller + 4 GPIO with I2C interface. More Info: https://store.ncd.io/product/4-channel-high-power-relay-controller-4-gpio-with-i2c-interface/?attribute_pa_choose-a-relay=20-amp-spdt", - "Adafruit__642": "Adafruit's high-temp, water-proof 1-wire temp sensor. More Info: https://www.adafruit.com/product/642", - "GridWorks__TSnap1": "Actual GridWorks TSnap 1.0 SCADA Box.", - "GridWorks__WaterTempHighPrecision": "Simulated temp sensor.", - "Gridworks__SimPm1": "Simulated power meter.", - "SchneiderElectric__Iem3455": "Schneider Electric IEM 344 utility meter.", - "GridWorks__SimBool30AmpRelay": "Simulated relay.", - "OpenEnergy__EmonPi": "Open Energy's open source multipurpose sensing device (including internal power meter). More Info: https://docs.openenergymonitor.org/emonpi/technical.html", - "GridWorks__SimTSnap1": "Simulated SCADA Box.", - "Atlas__EzFlo": "Atlas Scientific EZO Embedded Flow Meter Totalizer, pulse to I2C. More Info: https://drive.google.com/drive/u/0/folders/142bBV1pQIbMpyIR_0iRUr5gnzWgknOJp", - "Hubitat__C7__LAN1": "This refers to a Hubitat C7 that has been configured in a specific way with respect to the APIs it presents on the Local Area Network. The Hubitat C7 is a home automation hub that supports building ZigBee and ZWave meshes, plugs into Ethernet, has a reasonable user interface and has an active community of open-source developers who create drivers and package managers for devices, and supports the creation of various types of APIs on the Local Area Network. More Info: https://drive.google.com/drive/folders/1AqAU_lC2phzuI9XRYvogiIYA7GXNtlr6", - "GridWorks__Tank_Module_1": "This refers to a small module designed and assembled by GridWorks that is meant to be mounted to the side of a hot water tank. It requires 24V DC and has 4 temperature sensors coming out of it labeled 1, 2, 3 and 4. It is meant to provide temperature readings (taken within a half a second of each other) of all 4 of its sensors once a minute. More Info: https://drive.google.com/drive/folders/1GSxDd8Naf1GKK_fSOgQU933M1UcJ4r8q", - "Fibaro__Analog_Temp_Sensor": "This enum refers to a Fibaro FGBS-222 home automation device that has been configured in a specific way. This includes (1) being attached to two 10K NTC thermistors and a specific voltage divider circuit that specifies its temperature as a function of voltage and (2) one of its potential free outputs being in-line with the power of a partner Fibaro, so that it can power cycle its partner (because there are reports of Fibaros no longer reporting temp change after weeks or months until power cylced). The Fibaro itself is a tiny (29 X 18 X 13 mm) Z-Wave device powered on 9-30V DC that can read up to 6 1-wire DS18B20 temp sensors, 2 0-10V analog inputs and also has 2 potential free outputs. More Info: https://drive.google.com/drive/u/0/folders/1Muhsvw00goppHIfGSEmreX4hM6V78b-m", - "Amphenol__NTC_10K_Thermistor_MA100GG103BN": "A small gauge, low-cost, rapid response NTC 10K Thermistor designed for medical applications. More Info: https://drive.google.com/drive/u/0/folders/11HW4ov66UvxKAwqApW6IrtoXatZBLQkd", - "YHDC__SCT013-100": "YHDC current transformer More Info: https://en.yhdc.com/product/SCT013-401.html", - "Magnelab__SCT-0300-050": "Magnelab 50A current transformer", - "GridWorks__MultiTemp1": "GridWorks Analog temperature sensor that has 12 channels (labeled 1-12) to read 12 10K NTC Thermistors. It is comprised of 3 NCD ADS 1115 I2C temperature sensors with I2C Addresses 0x4b, 0x48, 0x49. More Info: https://drive.google.com/drive/u/0/folders/1OuY0tunaad2Ie4Id3zFB7FcbEwHizWuL", - "Krida__Emr16-I2c-V3": "16-Channel I2C Low Voltage Electromagnetic Relay Board More Info: https://drive.google.com/drive/u/0/folders/1jL82MTRKEh9DDmxJFQ2yU2cjqnVD9Ik7" - }, - "default_value": "UnknownMake__UnknownModel" -} diff --git a/docs/asls/json/spaceheat-node-gt.json b/docs/asls/json/spaceheat-node-gt.json deleted file mode 100644 index 0e3fcc8c..00000000 --- a/docs/asls/json/spaceheat-node-gt.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "spaceheat.node.gt", - "version": "100", - "owner": "gridworks@gridworks-consulting.com", - "description": "Spaceheat Node. A SpaceheatNode, or ShNode, is an organizing principal for the SCADA software. ShNodes can represent both underlying physical objects (water tank), measurements of these objects (temperature sensing at the top of a water tank), and actors within the code (an actor measuring multiple temperatures, or an actor responsible for filtering/smoothing temperature data for the purposes of thermostatic control).", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html", - "properties": { - "ShNodeId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "", - "required": true - }, - "Alias": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "required": true - }, - "ActorClass": { - "type": "string", - "format": "sh.actor.class", - "title": "", - "required": true - }, - "Role": { - "type": "string", - "format": "sh.node.role", - "title": "", - "required": true - }, - "DisplayName": { - "type": "string", - "required": false - }, - "ComponentId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Unique identifier for Component object articulated by the component.gt.000 type.", - "description": "Used if a Spaceheat Node is associated with a physical device.", - "required": false - }, - "ReportingSamplePeriodS": { - "type": "integer", - "required": false - }, - "RatedVoltageV": { - "type": "integer", - "format": "PositiveInteger", - "title": "", - "required": false - }, - "TypicalVoltageV": { - "type": "integer", - "format": "PositiveInteger", - "title": "", - "required": false - }, - "InPowerMetering": { - "type": "boolean", - "required": false - }, - "TypeName": { - "type": "string", - "value": "spaceheat.node.gt", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "100", - "required": true - } - }, - "formats": { - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "PositiveInteger": { - "type": "string", - "description": "Must be positive when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 1.7 will be interpreted as 1 and is also fine, while 0.5 is interpreted as 0 and will raise an exception.", - "example": "" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/spaceheat-telemetry-name.json b/docs/asls/json/spaceheat-telemetry-name.json deleted file mode 100644 index c42cdd10..00000000 --- a/docs/asls/json/spaceheat-telemetry-name.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "spaceheat.telemetry.name", - "enum_version": "001", - "description": "Specifies the name of sensed data reported by a Spaceheat SCADA", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/telemetry-name.html", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname", - "values": [ - "Unknown", - "PowerW", - "RelayState", - "WaterTempCTimes1000", - "WaterTempFTimes1000", - "GpmTimes100", - "CurrentRmsMicroAmps", - "GallonsTimes100", - "VoltageRmsMilliVolts", - "MilliWattHours", - "FrequencyMicroHz", - "AirTempCTimes1000", - "AirTempFTimes1000" - ], - "value_to_gt_symbol": { - "Unknown": "00000000", - "PowerW": "af39eec9", - "RelayState": "5a71d4b3", - "WaterTempCTimes1000": "c89d0ba1", - "WaterTempFTimes1000": "793505aa", - "GpmTimes100": "d70cce28", - "CurrentRmsMicroAmps": "ad19e79c", - "GallonsTimes100": "329a68c0", - "VoltageRmsMilliVolts": "bb6fdd59", - "MilliWattHours": "e0bb014b", - "FrequencyMicroHz": "337b8659", - "AirTempCTimes1000": "0f627faa", - "AirTempFTimes1000": "4c3f8c78" - }, - "value_to_version": { - "Unknown": "000", - "PowerW": "000", - "RelayState": "000", - "WaterTempCTimes1000": "000", - "WaterTempFTimes1000": "000", - "GpmTimes100": "000", - "CurrentRmsMicroAmps": "000", - "GallonsTimes100": "000", - "VoltageRmsMilliVolts": "001", - "MilliWattHours": "001", - "FrequencyMicroHz": "001", - "AirTempCTimes1000": "001", - "AirTempFTimes1000": "001" - }, - "value_descriptions": { - "Unknown": "Default Value - unknown telemetry name.", - "PowerW": "Power in Watts.", - "RelayState": "An associated read must be either 0 or 1, with 0 meaning that the relay is open and current CANNOT flow and 1 meaning that the relay is closed and current CAN flow. Note in particular that this TelemetryName is NOT meant to be used to reflect whether a relay is energized or de-energized and in particular '1' means the same thing for both Normally Open and Normally Closed relays. Also, it is not meant to be used for a double-throw relay.", - "WaterTempCTimes1000": "Water temperature, in Degrees Celcius multiplied by 1000. Example: 43200 means 43.2 deg Celcius.", - "WaterTempFTimes1000": "Water temperature, in Degrees F multiplied by 1000. Example: 142100 means 142.1 deg Fahrenheit.", - "GpmTimes100": "Gallons Per Minute multiplied by 100. Example: 433 means 4.33 gallons per minute.", - "CurrentRmsMicroAmps": "Current measurement in Root Mean Square MicroAmps.", - "GallonsTimes100": "Gallons multipled by 100. This is useful for flow meters that report cumulative gallons as their raw output. Example: 55300 means 55.3 gallons.", - "VoltageRmsMilliVolts": "Voltage in Root Mean Square MilliVolts.", - "MilliWattHours": "Energy in MilliWattHours.", - "FrequencyMicroHz": "Frequency in MicroHz. Example: 59,965,332 means 59.965332 Hz.", - "AirTempCTimes1000": "Air temperature, in Degrees Celsius multiplied by 1000. Example: 6234 means 6.234 deg Celcius.", - "AirTempFTimes1000": "Air temperature, in Degrees F multiplied by 1000. Example: 69329 means 69.329 deg Fahrenheit." - }, - "default_value": "Unknown" -} diff --git a/docs/asls/json/spaceheat-unit.json b/docs/asls/json/spaceheat-unit.json deleted file mode 100644 index 1fce1460..00000000 --- a/docs/asls/json/spaceheat-unit.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "gtr_asl": "001", - "enum_name": "spaceheat.unit", - "enum_version": "000", - "description": "Specifies the physical unit of sensed data reported by SCADA", - "ssot": "https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit", - "values": [ - "Unknown", - "Unitless", - "W", - "Celcius", - "Fahrenheit", - "Gpm", - "WattHours", - "AmpsRms", - "VoltsRms", - "Gallons" - ], - "value_to_gt_symbol": { - "Unknown": "00000000", - "Unitless": "ec972387", - "W": "f459a9c3", - "Celcius": "ec14bd47", - "Fahrenheit": "7d8832f8", - "Gpm": "b4580361", - "WattHours": "d66f1622", - "AmpsRms": "a969ac7c", - "VoltsRms": "e5d7555c", - "Gallons": "8e123a26" - }, - "value_to_version": { - "Unknown": "000", - "Unitless": "000", - "W": "000", - "Celcius": "000", - "Fahrenheit": "000", - "Gpm": "000", - "WattHours": "000", - "AmpsRms": "000", - "VoltsRms": "000", - "Gallons": "000" - }, - "value_descriptions": { - "Unknown": "", - "Unitless": "", - "W": "", - "Celcius": "", - "Fahrenheit": "", - "Gpm": "", - "WattHours": "", - "AmpsRms": "", - "VoltsRms": "", - "Gallons": "" - }, - "default_value": "Unknown" -} diff --git a/docs/asls/json/ta-data-channels.json b/docs/asls/json/ta-data-channels.json deleted file mode 100644 index 5d98d6fd..00000000 --- a/docs/asls/json/ta-data-channels.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "ta.data.channels", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Terminal Asset Data Channels. A list of data channels associated to a specific Terminal Asset.", - "properties": { - "TerminalAssetGNodeAlias": { - "type": "string", - "format": "LeftRightDot", - "title": "GNodeAlias for the Terminal Asset", - "description": "The Alias of the Terminal Asset about which the time series data is providing information.", - "required": true - }, - "TerminalAssetGNodeId": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "GNodeId for the Terminal Asset", - "description": "The immutable unique identifier for the Terminal Asset.", - "required": true - }, - "TimeUnixS": { - "type": "integer", - "format": "ReasonableUnixTimeS", - "title": "TimeUnixS", - "description": "The time that this list of data channels was created", - "required": true - }, - "Author": { - "type": "string", - "description": "Author of this list of data channels.", - "required": true - }, - "Channels": { - "type": "array", - "items": { - "type": "data.channel.000" - }, - "required": true - }, - "Identifier": { - "type": "string", - "format": "UuidCanonicalTextual", - "title": "Identifier", - "description": "Unique identifier for a specific instance of this type that can be used to establish how time series csv's were constructed.", - "required": true - }, - "TypeName": { - "type": "string", - "value": "ta.data.channels", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "formats": { - "ReasonableUnixTimeS": { - "type": "string", - "description": "Integer reflecting unix time seconds between 1970 and 3000", - "example": "" - }, - "UuidCanonicalTextual": { - "type": "string", - "description": "A string of hex words separated by hyphens of length 8-4-4-4-12.", - "example": "652ba6b0-c3bf-4f06-8a80-6b9832d60a25" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/telemetry-reporting-config.json b/docs/asls/json/telemetry-reporting-config.json deleted file mode 100644 index ff00a3a3..00000000 --- a/docs/asls/json/telemetry-reporting-config.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "telemetry.reporting.config", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "", - "properties": { - "TelemetryName": { - "type": "string", - "format": "spaceheat.telemetry.name", - "title": "", - "required": true - }, - "AboutNodeName": { - "type": "string", - "format": "LeftRightDot", - "title": "", - "description": "The name of the SpaceheatNode whose physical quantity is getting captured.", - "required": true - }, - "ReportOnChange": { - "type": "boolean", - "required": true - }, - "SamplePeriodS": { - "type": "integer", - "required": true - }, - "Exponent": { - "type": "integer", - "description": "Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C", - "required": true - }, - "Unit": { - "type": "string", - "format": "spaceheat.unit", - "title": "", - "required": true - }, - "AsyncReportThreshold": { - "type": "number", - "required": false - }, - "NameplateMaxValue": { - "type": "integer", - "format": "PositiveInteger", - "title": "", - "required": false - }, - "TypeName": { - "type": "string", - "value": "telemetry.reporting.config", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "Async reporting consistency", - "description": "If AsyncReportThreshold exists, so does NameplateMaxValue" - } - }, - "example": { - "TelemetryNameGtEnumSymbol": "af39eec9", - "AboutNodeName": "house-panel-power", - "ReportOnChange": true, - "SamplePeriodS": 300, - "Exponent": 0, - "UnitGtEnumSymbol": "f459a9c3", - "AsyncReportThreshold": 0.02, - "NameplateMaxValue": 3500, - "TypeName": "telemetry.reporting.config", - "Version": "000" - }, - "formats": { - "PositiveInteger": { - "type": "string", - "description": "Must be positive when interpreted as an integer. Interpretation as an integer follows the pydantic rules for this - which will round down rational numbers. So 1.7 will be interpreted as 1 and is also fine, while 0.5 is interpreted as 0 and will raise an exception.", - "example": "" - }, - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - } - } -} diff --git a/docs/asls/json/telemetry-snapshot-spaceheat.json b/docs/asls/json/telemetry-snapshot-spaceheat.json deleted file mode 100644 index 043b868c..00000000 --- a/docs/asls/json/telemetry-snapshot-spaceheat.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "gtr_asl": "001", - "type_name": "telemetry.snapshot.spaceheat", - "version": "000", - "owner": "gridworks@gridworks-consulting.com", - "description": "Snapshot of Telemetry Data from a SpaceHeat SCADA. A snapshot of all current sensed states, sent from a spaceheat SCADA to its AtomicTNode. The nth element of each of the three lists refer to the same reading (i.e., what is getting read, what the value is, what the TelemetryNames are.)", - "url": "https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node.html", - "properties": { - "ReportTimeUnixMs": { - "type": "integer", - "format": "ReasonableUnixTimeMs", - "title": "ReportTimeUnixMs", - "description": "The time, in unix ms, that the SCADA creates this type. It may not be when the SCADA sends the type to the atn (for example if Internet is down).", - "required": true - }, - "AboutNodeAliasList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "LeftRightDot", - "title": "AboutNodeAliases", - "description": "The list of Spaceheat nodes in the snapshot.", - "required": true - }, - "ValueList": { - "type": "array", - "items": { - "type": "integer" - }, - "required": true - }, - "TelemetryNameList": { - "type": "array", - "items": { - "type": "string" - }, - "format": "spaceheat.telemetry.name", - "title": "", - "required": true - }, - "TypeName": { - "type": "string", - "value": "telemetry.snapshot.spaceheat", - "title": "The type name" - }, - "Version": { - "type": "string", - "title": "The type version", - "default": "000", - "required": true - } - }, - "axioms": { - "Axiom1": { - "title": "ListLengthConsistency", - "description": "AboutNodeAliastList, ValueList and TelemetryNameList must all have the same length." - } - }, - "formats": { - "LeftRightDot": { - "type": "string", - "description": "Lowercase alphanumeric words separated by periods, with the most significant word (on the left) starting with an alphabet character.", - "example": "dw1.isone.me.freedom.apple" - }, - "ReasonableUnixTimeMs": { - "type": "string", - "description": "An integer reflecting unix time in MILLISECONDS between midnight Jan 1 2000 and midnight Jan 1 3000 UTC", - "example": "1702327940710" - } - } -} diff --git a/docs/asls/types.rst b/docs/asls/types.rst deleted file mode 100644 index 6a95a137..00000000 --- a/docs/asls/types.rst +++ /dev/null @@ -1,179 +0,0 @@ - -Type Application Shared Language (ASL) Specifications -=============== - -component.attribute.class.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/component-attribute-class-gt.json - -component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/component-gt.json - -data.channel.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/data-channel.json - -egauge.io.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/egauge-io.json - -egauge.register.config.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/egauge-register-config.json - -electric.meter.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/electric-meter-cac-gt.json - -electric.meter.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/electric-meter-component-gt.json - -fibaro.smart.implant.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/fibaro-smart-implant-cac-gt.json - -fibaro.smart.implant.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/fibaro-smart-implant-component-gt.json - -gt.dispatch.boolean.110 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-dispatch-boolean.json - -gt.dispatch.boolean.local.110 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-dispatch-boolean-local.json - -gt.driver.booleanactuator.cmd.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-driver-booleanactuator-cmd.json - -gt.sh.booleanactuator.cmd.status.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-booleanactuator-cmd-status.json - -gt.sh.cli.atn.cmd.110 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-cli-atn-cmd.json - -gt.sh.multipurpose.telemetry.status.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-multipurpose-telemetry-status.json - -gt.sh.simple.telemetry.status.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-simple-telemetry-status.json - -gt.sh.status.110 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-status.json - -gt.sh.telemetry.from.multipurpose.sensor.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-sh-telemetry-from-multipurpose-sensor.json - -gt.telemetry.110 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/gt-telemetry.json - -heartbeat.b.001 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/heartbeat-b.json - -hubitat.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-cac-gt.json - -hubitat.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-component-gt.json - -hubitat.poller.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-poller-cac-gt.json - -hubitat.poller.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-poller-component-gt.json - -hubitat.tank.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-tank-cac-gt.json - -hubitat.tank.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/hubitat-tank-component-gt.json - -multipurpose.sensor.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/multipurpose-sensor-cac-gt.json - -multipurpose.sensor.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/multipurpose-sensor-component-gt.json - -pipe.flow.sensor.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/pipe-flow-sensor-cac-gt.json - -pipe.flow.sensor.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/pipe-flow-sensor-component-gt.json - -power.watts.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/power-watts.json - -relay.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/relay-cac-gt.json - -relay.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/relay-component-gt.json - -resistive.heater.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/resistive-heater-cac-gt.json - -resistive.heater.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/resistive-heater-component-gt.json - -rest.poller.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/rest-poller-cac-gt.json - -rest.poller.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/rest-poller-component-gt.json - -simple.temp.sensor.cac.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/simple-temp-sensor-cac-gt.json - -simple.temp.sensor.component.gt.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/simple-temp-sensor-component-gt.json - -snapshot.spaceheat.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/snapshot-spaceheat.json - -spaceheat.node.gt.100 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/spaceheat-node-gt.json - -ta.data.channels.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/ta-data-channels.json - -telemetry.reporting.config.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/telemetry-reporting-config.json - -telemetry.snapshot.spaceheat.000 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: json/telemetry-snapshot-spaceheat.json diff --git a/docs/types/component-attribute-class-gt.rst b/docs/types/component-attribute-class-gt.rst deleted file mode 100644 index a1d0eac6..00000000 --- a/docs/types/component-attribute-class-gt.rst +++ /dev/null @@ -1,28 +0,0 @@ -ComponentAttributeClassGt -========================== -Python pydantic class corresponding to json type `component.attribute.class.gt`, version `000`. - -.. autoclass:: gwproto.types.ComponentAttributeClassGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). This identifier is used to associate a make/model with a specific component (i.e. the component will point to its ComponentAttributeClassId). - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: DisplayName. Optional Mutable field to include manufacturer's model name. Note that several different models may be given the same spaceheat.make.model enum name. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.component_attribute_class_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.ComponentAttributeClassGt_Maker - :members: diff --git a/docs/types/component-gt.rst b/docs/types/component-gt.rst deleted file mode 100644 index 06fff1b2..00000000 --- a/docs/types/component-gt.rst +++ /dev/null @@ -1,35 +0,0 @@ -ComponentGt -========================== -Python pydantic class corresponding to json type `component.gt`, version `000`. - -.. autoclass:: gwproto.types.ComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary identifier for components in all GridWorks registries. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: This is an optional, mutable field whose use is strongly encouraged. It may include information about HOW the component is used in a hardware layout. It may also include the HwUid for the component. - -**HwUid**: - - Description: Usually this is determined by the inheriting class. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.ComponentGt_Maker - :members: diff --git a/docs/types/data-channel-gt.rst b/docs/types/data-channel-gt.rst deleted file mode 100644 index 325d46da..00000000 --- a/docs/types/data-channel-gt.rst +++ /dev/null @@ -1,67 +0,0 @@ -DataChannelGt -========================== -Python pydantic class corresponding to json type `data.channel.gt`, version `001`. - -.. autoclass:: gwproto.types.DataChannelGt - :members: - -**Name**: - - Description: Name. The Channel Name is meant to be the local unique identifier for the channel within the context of a specific TerminalAsset. In addition to local uniqueness, it is immutable. It is designed to be the key that time series data is sorted by in analysis, as well as a useful way of referencing a channel within Scada code. - - Format: SpaceheatName - -**DisplayName**: - - Description: Display Name. This display name is the handle for the data channel. It is meant to be set by the person/people who will be analyzing time series data. It is only expected to be unique within the data channels associated to a specific Terminal Asset. Mutable. - -**AboutNodeName**: - - Description: About Name. The name of the SpaceheatNode whose physical quantities are getting captured. - - Format: SpaceheatName - -**CapturedByNodeName**: - - Description: Captured By Name. The name of the SpaceheatNode that is capturing the physical quantities (which can be AboutName but does not have to be). - - Format: SpaceheatName - -**TelemetryName**: - - Description: Telemetry Name. The name of the physical quantity getting measured. - -**TerminalAssetAlias**: - - Description: Terminal Asset. The Terminal Asset GNode for which this data channel is reporting data. For example, the GNode with alias hw1.isone.me.versant.keene.beech.ta represents the heat pump thermal storage system in the first GridWorks Millinocket deployment. - - Format: LeftRightDot - -**InPowerMetering**: - - Description: In Power Metering. This channel is in the sum of the aggregate transactive power metering for the terminal asset - -**StartS**: - - Description: Start Seconds Epoch Time. The epoch time of the first data record associated to a channel. If this value is None it means no known data yet. - - Format: UTCSeconds - -**Id**: - - Description: Id. Meant to be an immutable identifier that is globally unique (i.e., across terminal assets). - - Format: UUID4Str - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.data_channel_gt.check_is_u_t_c_seconds - :members: - - -.. autoclass:: gwproto.types.data_channel_gt.check_is_u_u_i_d4_str - :members: - - -.. autoclass:: gwproto.types.data_channel_gt.check_is_spaceheat_name - :members: - - -.. autoclass:: gwproto.types.data_channel_gt.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.DataChannelGt_Maker - :members: - diff --git a/docs/types/egauge-io.rst b/docs/types/egauge-io.rst deleted file mode 100644 index 5d7d0b15..00000000 --- a/docs/types/egauge-io.rst +++ /dev/null @@ -1,23 +0,0 @@ -EgaugeIo -========================== -Python pydantic class corresponding to json type `egauge.io`, version `000`. - -.. autoclass:: gwproto.types.EgaugeIo - :members: - -**InputConfig**: - - Description: Input config for one channel of data for a specific eGauge meter. This is the data available from the modbus csv map provided by eGauge for this component, for example http://egauge14875.egaug.es/6001C/settings.html for a eGauge device with ID 14875 - -**OutputConfig**: - - Description: Output config for the same channel . This is the data as the Scada proactor expects to consume it from the power meter driver proactor. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.EgaugeIo_Maker - :members: diff --git a/docs/types/egauge-register-config.rst b/docs/types/egauge-register-config.rst deleted file mode 100644 index 40ac413d..00000000 --- a/docs/types/egauge-register-config.rst +++ /dev/null @@ -1,35 +0,0 @@ -EgaugeRegisterConfig -========================== -Python pydantic class corresponding to json type `egauge.register.config`, version `000`. - -.. autoclass:: gwproto.types.EgaugeRegisterConfig - :members: - -**Address**: - - Description: Address. EGauge's modbus holding address. Note that the EGauge modbus map for holding address 100 will be 30100 - the '+30000' indicates it is a holding address. We use the 4-digit address after the '3'. - -**Name**: - - Description: Name. The name assigned in the EGauge's modbus map. This is configured by the user (see URL) - -**Description**: - - Description: Description. Again, assigned by the EGauge modbus map. Is usually 'change in value' - -**Type**: - - Description: Type. EGauge's numerical data type. Typically our power measurements are f32 ( 32-bit floating-point number). The serial number & firmware are t16 (which work to treat as 16-bit unsigned integer) and timestamps are u32 (32-bit unsigned integer). - -**Denominator**: - - Description: Denominator. Some of the modbus registers divide by 3.60E+06 (cumulative energy registers typically). For the power, current, voltage and phase angle the denominator is 1. - -**Unit**: - - Description: Unit. The EGauge unit - typically A, Hz, or W. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.EgaugeRegisterConfig_Maker - :members: diff --git a/docs/types/electric-meter-cac-gt.rst b/docs/types/electric-meter-cac-gt.rst deleted file mode 100644 index a837dd8c..00000000 --- a/docs/types/electric-meter-cac-gt.rst +++ /dev/null @@ -1,47 +0,0 @@ -ElectricMeterCacGt -========================== -Python pydantic class corresponding to json type `electric.meter.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.ElectricMeterCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: MakeModel. The brand name identifier for the electric meter (what you would specify in order to buy one). - -**DisplayName**: - - Description: Sample: EGauge 4030 - -**TelemetryNameList**: - - Description: TelemetryNames read by this power meter. - -**PollPeriodMs**: - - Description: Poll Period in Milliseconds. Poll Period refers to the period of time between two readings by the local actor. This is in contrast to Capture Period, which refers to the period between readings that are sent up to the cloud (or otherwise saved for the long-term). - -**Interface**: - - Description: - -**DefaultBaud**: - - Description: To be used when the comms method requires a baud rate. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.electric_meter_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.electric_meter_cac_gt.check_is_positive_integer - :members: - - -.. autoclass:: gwproto.types.ElectricMeterCacGt_Maker - :members: diff --git a/docs/types/electric-meter-component-gt.rst b/docs/types/electric-meter-component-gt.rst deleted file mode 100644 index 8942fa0d..00000000 --- a/docs/types/electric-meter-component-gt.rst +++ /dev/null @@ -1,56 +0,0 @@ -ElectricMeterComponentGt -========================== -Python pydantic class corresponding to json type `electric.meter.component.gt`, version `000`. - -.. autoclass:: gwproto.types.ElectricMeterComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of an ElectricMeter, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: Display Name for the Power Meter. Sample: Oak EGauge6074 - -**ConfigList**: - - Description: List of Data Channel configs . This power meter will produce multiple data channels. Each data channel measures a certain quantities (like power, current) for certain ShNodes (like a boost element or heat pump). - -**HwUid**: - - Description: Unique Hardware Id for the Power Meter. For eGauge, use what comes back over modbus address 100. - -**ModbusHost**: - - Description: Host on LAN when power meter is modbus over Ethernet. - -**ModbusPort**: - - Description: - - Format: NonNegativeInteger - -**EgaugeIoList**: - - Description: Bijecton from EGauge4030 input to ConfigList output. This should be empty unless the MakeModel of the corresponding component attribute class is EGauge 4030. The channels that can be read from an EGauge 4030 are configurable by the person who installs the device. The information is encapsulated in a modbus map provided by eGauge as a csv from a device-specific API. The EGaugeIoList maps the data from this map to the data that the SCADA expects to see. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.electric_meter_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.electric_meter_component_gt.check_is_positive_integer - :members: - - -.. autoclass:: gwproto.types.electric_meter_component_gt.check_is_non_negative_integer - :members: - - -.. autoclass:: gwproto.types.ElectricMeterComponentGt_Maker - :members: diff --git a/docs/types/fibaro-smart-implant-component-gt.rst b/docs/types/fibaro-smart-implant-component-gt.rst deleted file mode 100644 index 82d5d7ce..00000000 --- a/docs/types/fibaro-smart-implant-component-gt.rst +++ /dev/null @@ -1,38 +0,0 @@ -FibaroSmartImplantComponentGt -========================== -Python pydantic class corresponding to json type `fibaro.smart.implant.component.gt`, version `000`. - -.. autoclass:: gwproto.types.FibaroSmartImplantComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of an Fibaro, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**ZWaveDSK**: - - Description: The Z-Wave DSK (Device Specific Key) is a unique identifier associated with a Z-Wave device, used during the process of securely including the device into a Z-Wave network. It helps establish secure communication between the Z-Wave controller and the device, ensuring that only authorized devices can join the network. Unfortunately Hubitat does not currently provide a way to view the ZWave DSK of a Fibaro. - -**DisplayName**: - - Description: Sample: Fibaro Smart Implant 1010 A (For Fibaro A as opposed to B for GridWorks TankModule1 with Serial Number 1010). - -**HwUid**: - - Description: Hardware Unique Id. Use the Fibaro S2 PIN Code, which is printed on the back of each Fibaro Implant. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.fibaro_smart_implant_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.FibaroSmartImplantComponentGt_Maker - :members: diff --git a/docs/types/gt-dispatch-boolean-local.rst b/docs/types/gt-dispatch-boolean-local.rst deleted file mode 100644 index 98b87702..00000000 --- a/docs/types/gt-dispatch-boolean-local.rst +++ /dev/null @@ -1,45 +0,0 @@ -GtDispatchBooleanLocal -========================== -Python pydantic class corresponding to json type `gt.dispatch.boolean.local`, version `110`. - -.. autoclass:: gwproto.types.GtDispatchBooleanLocal - :members: - -**RelayState**: - - Description: Relay State (0 or 1). A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates the relay is CLOSED (on). Note that `0` means the relay is open whether or not the relay is normally open or normally closed (For a normally open relay, the relay is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.) - - Format: Bit - -**AboutNodeName**: - - Description: About Node Name. The boolean actuator Spaceheat Node getting turned on or off. - - Format: LeftRightDot - -**FromNodeName**: - - Description: From Node Name. The Spaceheat Node sending the command. - - Format: LeftRightDot - -**SendTimeUnixMs**: - - Description: Send Time in Unix Milliseconds. - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_dispatch_boolean_local.check_is_bit - :members: - - -.. autoclass:: gwproto.types.gt_dispatch_boolean_local.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_dispatch_boolean_local.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtDispatchBooleanLocal_Maker - :members: diff --git a/docs/types/gt-dispatch-boolean.rst b/docs/types/gt-dispatch-boolean.rst deleted file mode 100644 index ee3e8007..00000000 --- a/docs/types/gt-dispatch-boolean.rst +++ /dev/null @@ -1,57 +0,0 @@ -GtDispatchBoolean -========================== -Python pydantic class corresponding to json type `gt.dispatch.boolean`, version `110`. - -.. autoclass:: gwproto.types.GtDispatchBoolean - :members: - -**AboutNodeName**: - - Description: The Spaceheat Node getting dispatched. - - Format: LeftRightDot - -**ToGNodeAlias**: - - Description: GNodeAlias of the SCADA. - - Format: LeftRightDot - -**FromGNodeAlias**: - - Description: GNodeAlias of AtomicTNode. - - Format: LeftRightDot - -**FromGNodeInstanceId**: - - Description: GNodeInstance of the AtomicTNode. - - Format: UuidCanonicalTextual - -**RelayState**: - - Description: Relay State (0 or 1). A Relay State of `0` indicates the relay is OPEN (off). A Relay State of `1` indicates the relay is CLOSED (on). Note that `0` means the relay is open whether or not the relay is normally open or normally closed (For a normally open relay, the relay is ENERGIZED when it is in state `0` and DE-ENERGIZED when it is in state `1`.) - - Format: Bit - -**SendTimeUnixMs**: - - Description: Time the AtomicTNode sends the dispatch, by its clock. - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_dispatch_boolean.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.gt_dispatch_boolean.check_is_bit - :members: - - -.. autoclass:: gwproto.types.gt_dispatch_boolean.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_dispatch_boolean.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtDispatchBoolean_Maker - :members: diff --git a/docs/types/gt-driver-booleanactuator-cmd.rst b/docs/types/gt-driver-booleanactuator-cmd.rst deleted file mode 100644 index 4b29308e..00000000 --- a/docs/types/gt-driver-booleanactuator-cmd.rst +++ /dev/null @@ -1,41 +0,0 @@ -GtDriverBooleanactuatorCmd -========================== -Python pydantic class corresponding to json type `gt.driver.booleanactuator.cmd`, version `100`. - -.. autoclass:: gwproto.types.GtDriverBooleanactuatorCmd - :members: - -**RelayState**: - - Description: - - Format: Bit - -**ShNodeAlias**: - - Description: - - Format: LeftRightDot - -**CommandTimeUnixMs**: - - Description: - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_driver_booleanactuator_cmd.check_is_bit - :members: - - -.. autoclass:: gwproto.types.gt_driver_booleanactuator_cmd.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_driver_booleanactuator_cmd.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtDriverBooleanactuatorCmd_Maker - :members: diff --git a/docs/types/gt-sh-booleanactuator-cmd-status.rst b/docs/types/gt-sh-booleanactuator-cmd-status.rst deleted file mode 100644 index 68c4a823..00000000 --- a/docs/types/gt-sh-booleanactuator-cmd-status.rst +++ /dev/null @@ -1,36 +0,0 @@ -GtShBooleanactuatorCmdStatus -========================== -Python pydantic class corresponding to json type `gt.sh.booleanactuator.cmd.status`, version `100`. - -.. autoclass:: gwproto.types.GtShBooleanactuatorCmdStatus - :members: - -**ShNodeAlias**: - - Description: SpaceheatNodeAlias. The alias of the spaceheat node that is getting actuated. For example, `a.elt1.relay` would likely indicate the relay for a resistive element. - - Format: LeftRightDot - -**RelayStateCommandList**: - - Description: List of RelayStateCommands. - -**CommandTimeUnixMsList**: - - Description: List of Command Times. - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_booleanactuator_cmd_status.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_sh_booleanactuator_cmd_status.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtShBooleanactuatorCmdStatus_Maker - :members: diff --git a/docs/types/gt-sh-cli-atn-cmd.rst b/docs/types/gt-sh-cli-atn-cmd.rst deleted file mode 100644 index b0162458..00000000 --- a/docs/types/gt-sh-cli-atn-cmd.rst +++ /dev/null @@ -1,36 +0,0 @@ -GtShCliAtnCmd -========================== -Python pydantic class corresponding to json type `gt.sh.cli.atn.cmd`, version `110`. - -.. autoclass:: gwproto.types.GtShCliAtnCmd - :members: - -**FromGNodeAlias**: - - Description: GNodeAlias. Must be the SCADA's AtomicTNode. - - Format: LeftRightDot - -**SendSnapshot**: - - Description: Send Snapshot. Asks SCADA to send back a snapshot. For this version of the type, nothing would happen if SendSnapshot were set to False. However, we include this in case additional variations are added later. - -**FromGNodeId**: - - Description: GNodeId. - - Format: UuidCanonicalTextual - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_cli_atn_cmd.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.gt_sh_cli_atn_cmd.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.GtShCliAtnCmd_Maker - :members: diff --git a/docs/types/gt-sh-multipurpose-telemetry-status.rst b/docs/types/gt-sh-multipurpose-telemetry-status.rst deleted file mode 100644 index 60c594ce..00000000 --- a/docs/types/gt-sh-multipurpose-telemetry-status.rst +++ /dev/null @@ -1,42 +0,0 @@ -GtShMultipurposeTelemetryStatus -========================== -Python pydantic class corresponding to json type `gt.sh.multipurpose.telemetry.status`, version `100`. - -.. autoclass:: gwproto.types.GtShMultipurposeTelemetryStatus - :members: - -**AboutNodeAlias**: - - Description: AboutNodeAlias. The SpaceheatNode representing the physical object that the sensor reading is collecting data about. For example, a multipurpose temp sensor that reads 12 temperatures would have data for 12 different AboutNodeAliases, including say `a.tank1.temp1` for a temp sensor at the top of a water tank. - - Format: LeftRightDot - -**SensorNodeAlias**: - - Description: SensorNodeAlias. The alias of the SpaceheatNode representing the telemetry device - -**TelemetryName**: - - Description: TelemetryName. The TelemetryName of the readings. This is used to interpet the meaning of the reading values. For example, WaterTempCTimes1000 means the reading is measuring the a reading of 37 deg C. - -**ValueList**: - - Description: List of Values. The values of the readings. - -**ReadTimeUnixMsList**: - - Description: List of Read Times. The times that the MultipurposeSensor took the readings, in unix milliseconds - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_multipurpose_telemetry_status.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_sh_multipurpose_telemetry_status.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtShMultipurposeTelemetryStatus_Maker - :members: diff --git a/docs/types/gt-sh-simple-telemetry-status.rst b/docs/types/gt-sh-simple-telemetry-status.rst deleted file mode 100644 index 598bee82..00000000 --- a/docs/types/gt-sh-simple-telemetry-status.rst +++ /dev/null @@ -1,42 +0,0 @@ -GtShSimpleTelemetryStatus -========================== -Python pydantic class corresponding to json type `gt.sh.simple.telemetry.status`, version `100`. - -.. autoclass:: gwproto.types.GtShSimpleTelemetryStatus - :members: - -**ShNodeAlias**: - - Description: SpaceheatNodeAlias. The Alias of the SimpleSensor associated to the readings - - Format: LeftRightDot - -**TelemetryName**: - - Description: TelemetryName. The TelemetryName of the readings. This is used to interpet the meaning of the - reading values. For example, WaterTempCTimes1000 means the reading is measuring - the temperature of water, in Celsius multiplied by 1000. So a value of 37000 would be - a reading of 37 deg C. - -**ValueList**: - - Description: List of Values. The values of the readings. - -**ReadTimeUnixMsList**: - - Description: List of Read Times. The times that the SImpleSensor took the readings, in unix milliseconds - - Format: ReasonableUnixTimeMs - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_simple_telemetry_status.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_sh_simple_telemetry_status.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtShSimpleTelemetryStatus_Maker - :members: diff --git a/docs/types/gt-sh-status.rst b/docs/types/gt-sh-status.rst deleted file mode 100644 index 80f856bf..00000000 --- a/docs/types/gt-sh-status.rst +++ /dev/null @@ -1,61 +0,0 @@ -GtShStatus -========================== -Python pydantic class corresponding to json type `gt.sh.status`, version `110`. - -.. autoclass:: gwproto.types.GtShStatus - :members: - -**FromGNodeAlias**: - - Description: - - Format: LeftRightDot - -**FromGNodeId**: - - Description: - - Format: UuidCanonicalTextual - -**AboutGNodeAlias**: - - Description: - - Format: LeftRightDot - -**SlotStartUnixS**: - - Description: - - Format: ReasonableUnixTimeS - -**ReportingPeriodS**: - - Description: - -**SimpleTelemetryList**: - - Description: - -**MultipurposeTelemetryList**: - - Description: - -**BooleanactuatorCmdList**: - - Description: - -**StatusUid**: - - Description: - - Format: UuidCanonicalTextual - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_status.check_is_reasonable_unix_time_s - :members: - - -.. autoclass:: gwproto.types.gt_sh_status.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.gt_sh_status.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.GtShStatus_Maker - :members: diff --git a/docs/types/gt-sh-telemetry-from-multipurpose-sensor.rst b/docs/types/gt-sh-telemetry-from-multipurpose-sensor.rst deleted file mode 100644 index 61286708..00000000 --- a/docs/types/gt-sh-telemetry-from-multipurpose-sensor.rst +++ /dev/null @@ -1,39 +0,0 @@ -GtShTelemetryFromMultipurposeSensor -========================== -Python pydantic class corresponding to json type `gt.sh.telemetry.from.multipurpose.sensor`, version `100`. - -.. autoclass:: gwproto.types.GtShTelemetryFromMultipurposeSensor - :members: - -**ScadaReadTimeUnixMs**: - - Description: ScadaReadTime in Unix MilliSeconds. - - Format: ReasonableUnixTimeMs - -**AboutNodeAliasList**: - - Description: AboutNodeAliasList. List of aliases of the SpaceHeat Nodes getting measured - - Format: LeftRightDot - -**TelemetryNameList**: - - Description: TelemetryNameList. List of the TelemetryNames. The nth name in this list indicates the TelemetryName of the nth alias in the AboutNodeAliasList. - -**ValueList**: - - Description: ValueList. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_sh_telemetry_from_multipurpose_sensor.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.gt_sh_telemetry_from_multipurpose_sensor.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtShTelemetryFromMultipurposeSensor_Maker - :members: diff --git a/docs/types/gt-telemetry.rst b/docs/types/gt-telemetry.rst deleted file mode 100644 index be8dc59c..00000000 --- a/docs/types/gt-telemetry.rst +++ /dev/null @@ -1,34 +0,0 @@ -GtTelemetry -========================== -Python pydantic class corresponding to json type `gt.telemetry`, version `110`. - -.. autoclass:: gwproto.types.GtTelemetry - :members: - -**ScadaReadTimeUnixMs**: - - Description: Scada Read Time in Unix Milliseconds. - - Format: ReasonableUnixTimeMs - -**Value**: - - Description: Value. The value of the reading. - -**Name**: - - Description: Name. The name of the Simple Sensing Spaceheat Node. This is both the AboutNodeName and FromNodeName for a data channel. The TelemetryName (and thus Units) are expected to be inferred by the Spaceheat Node. For example this is done initially in SCADA code according to whether the component of the Node is a PipeFlowSensorComponent, SimpleTempSensorComponent etc. - -**Exponent**: - - Description: Exponent. Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.gt_telemetry.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.GtTelemetry_Maker - :members: diff --git a/docs/types/heartbeat-b.rst b/docs/types/heartbeat-b.rst deleted file mode 100644 index eb926125..00000000 --- a/docs/types/heartbeat-b.rst +++ /dev/null @@ -1,60 +0,0 @@ -HeartbeatB -========================== -Python pydantic class corresponding to json type `heartbeat.b`, version `001`. - -.. autoclass:: gwproto.types.HeartbeatB - :members: - -**FromGNodeAlias**: - - Description: My GNodeAlias. - - Format: LeftRightDot - -**FromGNodeInstanceId**: - - Description: My GNodeInstanceId. - - Format: UuidCanonicalTextual - -**MyHex**: - - Description: Hex character getting sent. - - Format: HexChar - -**YourLastHex**: - - Description: Last hex character received from heartbeat partner.. - - Format: HexChar - -**LastReceivedTimeUnixMs**: - - Description: Time YourLastHex was received on my clock. - - Format: ReasonableUnixTimeMs - -**SendTimeUnixMs**: - - Description: Time this message is made and sent on my clock. - - Format: ReasonableUnixTimeMs - -**StartingOver**: - - Description: True if the heartbeat initiator wants to start the volley over. (typically the AtomicTNode in an AtomicTNode / SCADA pair) wants to start the heartbeating volley over. The result is that its partner will not expect the initiator to know its last Hex. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.heartbeat_b.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.heartbeat_b.check_is_hex_char - :members: - - -.. autoclass:: gwproto.types.heartbeat_b.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.heartbeat_b.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.HeartbeatB_Maker - :members: diff --git a/docs/types/hubitat-component-gt.rst b/docs/types/hubitat-component-gt.rst deleted file mode 100644 index f63d80ce..00000000 --- a/docs/types/hubitat-component-gt.rst +++ /dev/null @@ -1,34 +0,0 @@ -HubitatComponentGt -========================== -Python pydantic class corresponding to json type `hubitat.component.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a Hubitat, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**Hubitat**: - - Description: Hubitat Type Helper. Includes the information needed to access the MakerAPI of a Hubitat on the Local area network: Host, MakerApiID, AccessToken and MacAddress for the Hubitat. - -**DisplayName**: - - Description: Sample: Oak Hubitat 81:37:82 (using the last 6 digits of the Hubitat MacId in the display name, as well as the short alias for the associated g node.) - -**HwUid**: - - Description: Hardware Unique Id. Use the final 6 characters of the Hubitat mac address. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.HubitatComponentGt_Maker - :members: diff --git a/docs/types/hubitat-poller-component-gt.rst b/docs/types/hubitat-poller-component-gt.rst deleted file mode 100644 index 93e66a4f..00000000 --- a/docs/types/hubitat-poller-component-gt.rst +++ /dev/null @@ -1,37 +0,0 @@ -HubitatPollerComponentGt -========================== -Python pydantic class corresponding to json type `hubitat.poller.component.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatPollerComponentGt - :members: - -**ComponentId**: - - Description: ComponentId. - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: DisplayName. Sample: Downstairs Thermostat - -**HwUid**: - - Description: HwUid. Unique Hardware Identifier - -**Poller**: - - Description: Poller. Includes hubitat_component_id (str), device_id (int), enabled (bool), poll_period_s (int) and attributes. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.hubitat_poller_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.HubitatPollerComponentGt_Maker - :members: diff --git a/docs/types/hubitat-tank-component-gt.rst b/docs/types/hubitat-tank-component-gt.rst deleted file mode 100644 index 44fba7ca..00000000 --- a/docs/types/hubitat-tank-component-gt.rst +++ /dev/null @@ -1,34 +0,0 @@ -HubitatTankComponentGt -========================== -Python pydantic class corresponding to json type `hubitat.tank.component.gt`, version `000`. - -.. autoclass:: gwproto.types.HubitatTankComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a GridWorks TankModule1 and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**Tank**: - - Description: Tank. The configuration information (HubitatTankSettingsGt) about the 4 analog temperature sensors for a GridWorks TankModule1. - -**DisplayName**: - - Description: Sample: GridWorks TankModule SN 1010 - -**HwUid**: - - Description: Hardware Unique Id. Use the GridWorks Serial number for GridWorks TankModule1. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.HubitatTankComponentGt_Maker - :members: diff --git a/docs/types/multipurpose-sensor-cac-gt.rst b/docs/types/multipurpose-sensor-cac-gt.rst deleted file mode 100644 index bc687566..00000000 --- a/docs/types/multipurpose-sensor-cac-gt.rst +++ /dev/null @@ -1,49 +0,0 @@ -MultipurposeSensorCacGt -========================== -Python pydantic class corresponding to json type `multipurpose.sensor.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.MultipurposeSensorCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: MakeModel. Meant to be enough to articulate any difference in how GridWorks code would interact with a device. Should be able to use this information to buy or build a device. - -**PollPeriodMs**: - - Description: Poll Period in Milliseconds. Poll Period refers to the period of time between two readings by the local actor. This is in contrast to Capture Period, which refers to the period between readings that are sent up to the cloud (or otherwise saved for the long-term). - -**Exponent**: - - Description: Exponent. Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C - -**TempUnit**: - - Description: Temp Unit. - -**TelemetryNameList**: - - Description: - -**MaxThermistors**: - - Description: The maximum number of temperature sensors this multipurpose sensor can read. - -**DisplayName**: - - Description: Sample: GridWorks TSnap1.0 as 12-channel analog temp sensor - -**CommsMethod**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.multipurpose_sensor_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.MultipurposeSensorCacGt_Maker - :members: diff --git a/docs/types/multipurpose-sensor-component-gt.rst b/docs/types/multipurpose-sensor-component-gt.rst deleted file mode 100644 index 0af90591..00000000 --- a/docs/types/multipurpose-sensor-component-gt.rst +++ /dev/null @@ -1,45 +0,0 @@ -MultipurposeSensorComponentGt -========================== -Python pydantic class corresponding to json type `multipurpose.sensor.component.gt`, version `000`. - -.. autoclass:: gwproto.types.MultipurposeSensorComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a MultipurposeSensor (perhaps only the 12-channel analog temp sensor), and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**ChannelList**: - - Description: - -**ConfigList**: - - Description: - -**HwUid**: - - Description: Hardware Unique Id. - -**DisplayName**: - - Description: Sample: Oak Multipurpose Temp Sensor Component <100> - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.multipurpose_sensor_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.multipurpose_sensor_component_gt.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.MultipurposeSensorComponentGt_Maker - :members: diff --git a/docs/types/power-watts.rst b/docs/types/power-watts.rst deleted file mode 100644 index 5acd7ac3..00000000 --- a/docs/types/power-watts.rst +++ /dev/null @@ -1,20 +0,0 @@ -PowerWatts -========================== -Python pydantic class corresponding to json type `power.watts`, version `000`. - -.. autoclass:: gwproto.types.PowerWatts - :members: - -**Watts**: - - Description: Current Power in Watts. - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.PowerWatts_Maker - :members: diff --git a/docs/types/resistive-heater-cac-gt.rst b/docs/types/resistive-heater-cac-gt.rst deleted file mode 100644 index 865359aa..00000000 --- a/docs/types/resistive-heater-cac-gt.rst +++ /dev/null @@ -1,42 +0,0 @@ -ResistiveHeaterCacGt -========================== -Python pydantic class corresponding to json type `resistive.heater.cac.gt`, version `000`. - -.. autoclass:: gwproto.types.ResistiveHeaterCacGt - :members: - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class (aka 'cac' or Component Attribute Class). Authority is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**MakeModel**: - - Description: - -**DisplayName**: - - Description: - -**NameplateMaxPowerW**: - - Description: - -**RatedVoltageV**: - - Description: - - Format: PositiveInteger - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.resistive_heater_cac_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.resistive_heater_cac_gt.check_is_positive_integer - :members: - - -.. autoclass:: gwproto.types.ResistiveHeaterCacGt_Maker - :members: diff --git a/docs/types/resistive-heater-component-gt.rst b/docs/types/resistive-heater-component-gt.rst deleted file mode 100644 index fd874ed7..00000000 --- a/docs/types/resistive-heater-component-gt.rst +++ /dev/null @@ -1,41 +0,0 @@ -ResistiveHeaterComponentGt -========================== -Python pydantic class corresponding to json type `resistive.heater.component.gt`, version `000`. - -.. autoclass:: gwproto.types.ResistiveHeaterComponentGt - :members: - -**ComponentId**: - - Description: Component Id. Primary GridWorks identifier for a specific physical instance of a ResistiveHeater, and also as a more generic Component. - - Format: UuidCanonicalTextual - -**ComponentAttributeClassId**: - - Description: ComponentAttributeClassId. Unique identifier for the device class. Authority for these, as well as the relationship between Components and ComponentAttributeClasses (Cacs) is maintained by the World Registry. - - Format: UuidCanonicalTextual - -**DisplayName**: - - Description: - -**HwUid**: - - Description: Hardware Unique Id. - -**TestedMaxHotMilliOhms**: - - Description: - -**TestedMaxColdMilliOhms**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.resistive_heater_component_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.ResistiveHeaterComponentGt_Maker - :members: diff --git a/docs/types/rest-poller-component-gt.rst b/docs/types/rest-poller-component-gt.rst deleted file mode 100644 index 741ec3a4..00000000 --- a/docs/types/rest-poller-component-gt.rst +++ /dev/null @@ -1,17 +0,0 @@ -RestPollerComponentGt -========================== -Python pydantic class corresponding to json type `rest.poller.component.gt`, version `000`. - -.. autoclass:: gwproto.types.RestPollerComponentGt - :members: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.RestPollerComponentGt_Maker - :members: diff --git a/docs/types/snapshot-spaceheat.rst b/docs/types/snapshot-spaceheat.rst deleted file mode 100644 index 6e066b0b..00000000 --- a/docs/types/snapshot-spaceheat.rst +++ /dev/null @@ -1,36 +0,0 @@ -SnapshotSpaceheat -========================== -Python pydantic class corresponding to json type `snapshot.spaceheat`, version `000`. - -.. autoclass:: gwproto.types.SnapshotSpaceheat - :members: - -**FromGNodeAlias**: - - Description: - - Format: LeftRightDot - -**FromGNodeInstanceId**: - - Description: - - Format: UuidCanonicalTextual - -**Snapshot**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.snapshot_spaceheat.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.snapshot_spaceheat.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.SnapshotSpaceheat_Maker - :members: diff --git a/docs/types/spaceheat-node-gt.rst b/docs/types/spaceheat-node-gt.rst deleted file mode 100644 index b44ebfe7..00000000 --- a/docs/types/spaceheat-node-gt.rst +++ /dev/null @@ -1,64 +0,0 @@ -SpaceheatNodeGt -========================== -Python pydantic class corresponding to json type `spaceheat.node.gt`, version `100`. - -.. autoclass:: gwproto.types.SpaceheatNodeGt - :members: - -**ShNodeId**: - - Description: - - Format: UuidCanonicalTextual - -**Alias**: - - Description: - - Format: LeftRightDot - -**ActorClass**: - - Description: - -**Role**: - - Description: - -**DisplayName**: - - Description: - -**ComponentId**: - - Description: Unique identifier for Spaceheat Node's Component. Used if a Spaceheat Node is associated with a physical device. - - Format: UuidCanonicalTextual - -**ReportingSamplePeriodS**: - - Description: - -**RatedVoltageV**: - - Description: - - Format: PositiveInteger - -**TypicalVoltageV**: - - Description: - - Format: PositiveInteger - -**InPowerMetering**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.spaceheat_node_gt.check_is_uuid_canonical_textual - :members: - - -.. autoclass:: gwproto.types.spaceheat_node_gt.check_is_positive_integer - :members: - - -.. autoclass:: gwproto.types.spaceheat_node_gt.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.SpaceheatNodeGt_Maker - :members: diff --git a/docs/types/telemetry-reporting-config.rst b/docs/types/telemetry-reporting-config.rst deleted file mode 100644 index 7ea79735..00000000 --- a/docs/types/telemetry-reporting-config.rst +++ /dev/null @@ -1,51 +0,0 @@ -TelemetryReportingConfig -========================== -Python pydantic class corresponding to json type `telemetry.reporting.config`, version `000`. - -.. autoclass:: gwproto.types.TelemetryReportingConfig - :members: - -**TelemetryName**: - - Description: - -**AboutNodeName**: - - Description: The name of the SpaceheatNode whose physical quantity is getting captured. - - Format: LeftRightDot - -**ReportOnChange**: - - Description: - -**SamplePeriodS**: - - Description: - -**Exponent**: - - Description: Exponent. Say the TelemetryName is WaterTempCTimes1000; this corresponds to units of Celsius. To match the implication in the name, the Exponent should be 3, and a Value of 65300 would indicate 65.3 deg C - -**Unit**: - - Description: - -**AsyncReportThreshold**: - - Description: - -**NameplateMaxValue**: - - Description: - - Format: PositiveInteger - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.telemetry_reporting_config.check_is_positive_integer - :members: - - -.. autoclass:: gwproto.types.telemetry_reporting_config.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.TelemetryReportingConfig_Maker - :members: diff --git a/docs/types/telemetry-snapshot-spaceheat.rst b/docs/types/telemetry-snapshot-spaceheat.rst deleted file mode 100644 index 1986d23e..00000000 --- a/docs/types/telemetry-snapshot-spaceheat.rst +++ /dev/null @@ -1,39 +0,0 @@ -TelemetrySnapshotSpaceheat -========================== -Python pydantic class corresponding to json type `telemetry.snapshot.spaceheat`, version `000`. - -.. autoclass:: gwproto.types.TelemetrySnapshotSpaceheat - :members: - -**ReportTimeUnixMs**: - - Description: ReportTimeUnixMs. The time, in unix ms, that the SCADA creates this type. It may not be when the SCADA sends the type to the atn (for example if Internet is down). - - Format: ReasonableUnixTimeMs - -**AboutNodeAliasList**: - - Description: AboutNodeAliases. The list of Spaceheat nodes in the snapshot. - - Format: LeftRightDot - -**ValueList**: - - Description: ValueList. - -**TelemetryNameList**: - - Description: - -**TypeName**: - - Description: All GridWorks Versioned Types have a fixed TypeName, which is a string of lowercase alphanumeric words separated by periods, most significant word (on the left) starting with an alphabet character, and final word NOT all Hindu-Arabic numerals. - -**Version**: - - Description: All GridWorks Versioned Types have a fixed version, which is a string of three Hindu-Arabic numerals. - - - -.. autoclass:: gwproto.types.telemetry_snapshot_spaceheat.check_is_left_right_dot - :members: - - -.. autoclass:: gwproto.types.telemetry_snapshot_spaceheat.check_is_reasonable_unix_time_ms - :members: - - -.. autoclass:: gwproto.types.TelemetrySnapshotSpaceheat_Maker - :members: diff --git a/src/gwproto/data_classes/hardware_layout.py b/src/gwproto/data_classes/hardware_layout.py index 5ca74062..d9687210 100644 --- a/src/gwproto/data_classes/hardware_layout.py +++ b/src/gwproto/data_classes/hardware_layout.py @@ -32,7 +32,7 @@ default_cac_decoder, default_component_decoder, ) -from gwproto.enums import ActorClass, Role, TelemetryName +from gwproto.enums import ActorClass, TelemetryName from gwproto.types import ( ComponentAttributeClassGt, ComponentGt, @@ -192,7 +192,7 @@ def load_nodes( errors = [] for node_dict in layout.get("ShNodes", []): try: - node_name = node_dict["Alias"] + node_name = node_dict["Name"] if included_node_names is None or node_name in included_node_names: nodes[node_name] = cls.make_node(node_dict, components) except Exception as e: # noqa: PERF203 @@ -245,9 +245,9 @@ def check_node_channel_consistency( dc for dc in data_channels.values() if dc.Name in my_channel_names ] for channel in my_channels: - if channel.CapturedByNodeName != node.Alias: + if channel.CapturedByNodeName != node.Name: raise DcError( - f"Channel {channel} should have CapturedByNodeName {node.Alias}" + f"Channel {channel} should have CapturedByNodeName {node.Name}" ) @classmethod @@ -384,11 +384,11 @@ def resolve_links( component = components.get(node.component_id, None) if component is None: raise DcError( # noqa: TRY301 - f"{node.alias} component {node.component_id} not loaded!" + f"{node.name} component {node.component_id} not loaded!" ) if isinstance(component, ComponentResolver): component.resolve( - node.alias, + node.name, nodes, components, ) @@ -414,7 +414,7 @@ def __init__( self.components_by_type[type(component)].append(component) self.nodes = dict(nodes) self.nodes_by_component = { - node.component_id: node.alias for node in self.nodes.values() + node.component_id: node.name for node in self.nodes.values() } self.data_channels = dict(data_channels) @@ -557,14 +557,14 @@ def load_dict( # noqa: PLR0913 def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN401 return self.data_channels.get(name, default) - def node(self, alias: str, default: Any = None) -> ShNode: # noqa: ANN401 - return self.nodes.get(alias, default) + def node(self, name: str, default: Any = None) -> ShNode: # noqa: ANN401 + return self.nodes.get(name, default) - def component(self, node_alias: str) -> Optional[Component]: - return self.component_from_node(self.node(node_alias, None)) + def component(self, node_name: str) -> Optional[Component]: + return self.component_from_node(self.node(node_name, None)) - def cac(self, node_alias: str) -> Optional[ComponentAttributeClassGt]: - component = self.component(node_alias) + def cac(self, node_name: str) -> Optional[ComponentAttributeClassGt]: + component = self.component(node_name) if component is None: return None return component.cac @@ -601,22 +601,22 @@ def component_from_node(self, node: Optional[ShNode]) -> Optional[Component]: ) @classmethod - def parent_alias(cls, alias: str) -> str: - last_delimiter = alias.rfind(".") + def parent_hierarchy_name(cls, hierarchy_name: str) -> str: + last_delimiter = hierarchy_name.rfind(".") if last_delimiter == -1: return "" - return alias[:last_delimiter] + return hierarchy_name[:last_delimiter] - def parent_node(self, alias: str) -> Optional[ShNode]: - parent_alias = self.parent_alias(alias) - if not parent_alias: + def parent_node(self, hierarchy_name: str) -> Optional[ShNode]: + h_name = self.parent_hierarchy_name(hierarchy_name) + if not h_name: return None - if parent_alias not in self.nodes: - raise DcError(f"{alias} is missing parent {parent_alias}!") - return self.node(parent_alias) - - def descendants(self, alias: str) -> List[ShNode]: - return list(filter(lambda x: x.alias.startswith(alias), self.nodes.values())) + parent = next( + (n for n in self.nodes.values() if n.ActorHierarchyName == h_name), None + ) + if parent is None: + raise DcError(f"{hierarchy_name} is missing parent {h_name}!") + return self.node(h_name) @cached_property def atn_g_node_alias(self) -> str: @@ -684,8 +684,9 @@ def all_power_meter_telemetry_tuples(self) -> List[TelemetryTuple]: @cached_property def power_meter_node(self) -> ShNode: - """Schema for input data enforces exactly one Spaceheat Node with role PowerMeter""" - return next(filter(lambda x: x.role == Role.PowerMeter, self.nodes.values())) + return next( + filter(lambda x: x.ActorClass == ActorClass.PowerMeter, self.nodes.values()) + ) @cached_property def power_meter_component(self) -> ElectricMeterComponent: @@ -704,51 +705,6 @@ def power_meter_cac(self) -> ElectricMeterCacGt: ) return self.power_meter_node.component.cac # type: ignore[union-attr, return-value] - @cached_property - def all_resistive_heaters(self) -> List[ShNode]: - all_nodes = list(self.nodes.values()) - return list(filter(lambda x: (x.role == Role.BoostElement), all_nodes)) - - @cached_property - def scada_node(self) -> ShNode: - """Schema for input data enforces exactly one Spaceheat Node with role Scada""" - nodes = list(filter(lambda x: x.role == Role.Scada, self.nodes.values())) - return nodes[0] - - @cached_property - def home_alone_node(self) -> ShNode: - """Schema for input data enforces exactly one Spaceheat Node with role HomeAlone""" - nodes = list(filter(lambda x: x.role == Role.HomeAlone, self.nodes.values())) - return nodes[0] - - @cached_property - def my_home_alone(self) -> ShNode: - all_nodes = list(self.nodes.values()) - home_alone_nodes = list(filter(lambda x: (x.role == Role.HomeAlone), all_nodes)) - if len(home_alone_nodes) != 1: - raise ValueError( - "there should be a single SpaceheatNode with role HomeAlone" - ) - return home_alone_nodes[0] - - @cached_property - def my_boolean_actuators(self) -> List[ShNode]: - all_nodes = list(self.nodes.values()) - return list(filter(lambda x: (x.role == Role.BooleanActuator), all_nodes)) - - @cached_property - def my_simple_sensors(self) -> List[ShNode]: - all_nodes = list(self.nodes.values()) - return list( - filter( - lambda x: ( - x.actor_class - in {ActorClass.SimpleSensor, ActorClass.BooleanActuator} - ), - all_nodes, - ) - ) - @cached_property def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: multi_nodes = list( @@ -781,16 +737,6 @@ def all_multipurpose_telemetry_tuples(self) -> List[TelemetryTuple]: ) return telemetry_tuples - @cached_property - def my_multipurpose_sensors(self) -> List[ShNode]: - """This will be a list of all sensing devices that either measure more - than one ShNode or measure more than one physical quantity type (or both). - This includes the (unique) power meter, but may also include other roles like thermostats - and heat pumps.""" - all_nodes = list(self.nodes.values()) - multi_purpose_roles = [Role.PowerMeter, Role.MultiChannelAnalogTempSensor] - return list(filter(lambda x: (x.role in multi_purpose_roles), all_nodes)) - @cached_property def my_telemetry_tuples(self) -> List[TelemetryTuple]: """This will include telemetry tuples from all the multipurpose sensors, the most diff --git a/src/gwproto/data_classes/sh_node.py b/src/gwproto/data_classes/sh_node.py index 772b791a..9be6f778 100644 --- a/src/gwproto/data_classes/sh_node.py +++ b/src/gwproto/data_classes/sh_node.py @@ -5,7 +5,7 @@ from pydantic import ConfigDict from gwproto.data_classes.components.component import Component -from gwproto.enums import ActorClass, Role +from gwproto.enums import ActorClass from gwproto.types import SpaceheatNodeGt @@ -29,17 +29,13 @@ def sh_node_id(self) -> str: return self.ShNodeId @property - def alias(self) -> str: - return self.Alias + def name(self) -> str: + return self.Name @property def actor_class(self) -> ActorClass: return self.ActorClass - @property - def role(self) -> Role: - return self.Role - @property def display_name(self) -> Optional[str]: return self.DisplayName @@ -48,18 +44,14 @@ def display_name(self) -> Optional[str]: def component_id(self) -> Optional[str]: return self.ComponentId - @property - def reporting_sample_period_s(self) -> Optional[int]: - return self.ReportingSamplePeriodS - @property def in_power_metering(self) -> Optional[bool]: return self.InPowerMetering def __repr__(self) -> str: - rs = f"ShNode {self.display_name} => {self.role} {self.alias}, " + rs = f"ShNode {self.display_name} => {self.name}, " if self.has_actor: - rs += " (has actor)" + rs += f" ({self.actor_class})" else: rs += " (passive, no actor)" return rs diff --git a/src/gwproto/data_classes/telemetry_tuple.py b/src/gwproto/data_classes/telemetry_tuple.py index 43e1126f..9c4aefb7 100644 --- a/src/gwproto/data_classes/telemetry_tuple.py +++ b/src/gwproto/data_classes/telemetry_tuple.py @@ -13,7 +13,7 @@ class TelemetryTuple(NamedTuple): TelemetryName: TelemetryName def __repr__(self) -> str: - return f"TT({self.AboutNode.alias} {self.TelemetryName} read by {self.SensorNode.alias})" + return f"TT({self.AboutNode.name} {self.TelemetryName} read by {self.SensorNode.name})" class ChannelStub(BaseModel): diff --git a/src/gwproto/enums/__init__.py b/src/gwproto/enums/__init__.py index 595bfbfc..ea26aa17 100644 --- a/src/gwproto/enums/__init__.py +++ b/src/gwproto/enums/__init__.py @@ -35,7 +35,6 @@ from gwproto.enums.actor_class import ActorClass from gwproto.enums.local_comm_interface import LocalCommInterface from gwproto.enums.make_model import MakeModel -from gwproto.enums.role import Role from gwproto.enums.telemetry_name import TelemetryName from gwproto.enums.thermistor_data_method import ThermistorDataMethod from gwproto.enums.unit import Unit @@ -44,7 +43,6 @@ "ActorClass", # [sh.actor.class.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shactorclass) "LocalCommInterface", # [local.comm.interface.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#localcomminterface) "MakeModel", # [spaceheat.make.model.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel) - "Role", # [sh.node.role.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole) "TelemetryName", # [spaceheat.telemetry.name.001](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheattelemetryname) "ThermistorDataMethod", "Unit", # [spaceheat.unit.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatunit) diff --git a/src/gwproto/enums/role.py b/src/gwproto/enums/role.py deleted file mode 100644 index c2584ddb..00000000 --- a/src/gwproto/enums/role.py +++ /dev/null @@ -1,84 +0,0 @@ -from enum import auto - -from gw.enums import GwStrEnum - - -class Role(GwStrEnum): - """ - Categorizes SpaceheatNodes by their function within the heating system - - Enum sh.node.role version 000 in the GridWorks Type registry. - - Used by used by multiple Application Shared Languages (ASLs), including but not limited to - gwproto. For more information: - - [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/) - - [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shnoderole) - - [More Info](https://gridworks-protocol.readthedocs.io/en/latest/spaceheat-node-role.html) - - Values (with symbols in parens): - - Unknown (00000000): Unknown Role - - Scada (d0afb424): Primary SCADA - - HomeAlone (863e50d1): HomeAlone GNode - - Atn (6ddff83b): AtomicTNode - - PowerMeter (9ac68b6e): A SpaceheatNode representing the power meter that is used to settle - financial transactions with the TerminalAsset. That is, this is the power meter whose - accuracy is certified in the creation of the TerminalAsset GNode via creation of the - TaDeed. [More Info](https://gridworks.readthedocs.io/en/latest/terminal-asset.html). - - BoostElement (99c5f326): Resistive element used for providing heat to a thermal store. - - BooleanActuator (57b788ee): A solid state or mechanical relay with two states (open, closed) - - DedicatedThermalStore (3ecfe9b8): A dedicated thermal store within a thermal storage heating - system - could be one or more water tanks, phase change material, etc. - - TankWaterTempSensor (73308a1f): A temperature sensor used for measuring temperature inside - or on the immediate outside of a water tank. - - PipeTempSensor (c480f612): A temperature sensor used for measuring the temperature of a tank. - Typically curved metal thermistor with thermal grease for good contact. - - RoomTempSensor (fec74958): A temperature sensor used for measuring room temperature, or temp - in a heated space more generally. - - OutdoorTempSensor (5938bf1f): A temperature sensor used for measuring outdoor temperature. - - PipeFlowMeter (ece3b600): A meter that measures flow of liquid through a pipe, in units of - VOLUME/TIME - - HeatedSpace (65725f44): A Heated Space. - - HydronicPipe (fe3cbdd5): A pipe carrying techinical water or other fluid (e.g. glycol) in - a heating system. - - BaseboardRadiator (05fdd645): A baseboard radiator - one kind of emitter in a hydronic heating - system. - - RadiatorFan (6896109b): A fan that can amplify the power out of a radiator. - - CirculatorPump (b0eaf2ba): Circulator pump for one or more of the hydronic pipe loops - - MultiChannelAnalogTempSensor (661d7e73): An analog multi channel temperature sensor - - Outdoors (dd975b31): The outdoors - """ - - Unknown = auto() - Scada = auto() - HomeAlone = auto() - Atn = auto() - PowerMeter = auto() - BoostElement = auto() - BooleanActuator = auto() - DedicatedThermalStore = auto() - TankWaterTempSensor = auto() - PipeTempSensor = auto() - RoomTempSensor = auto() - OutdoorTempSensor = auto() - PipeFlowMeter = auto() - HeatedSpace = auto() - HydronicPipe = auto() - BaseboardRadiator = auto() - RadiatorFan = auto() - CirculatorPump = auto() - MultiChannelAnalogTempSensor = auto() - Outdoors = auto() - - @classmethod - def default(cls) -> "Role": - """ - Returns default value (in this case Unknown) - """ - return cls.Unknown - - @classmethod - def enum_name(cls) -> str: - """ - The name in the GridWorks Type Registry (sh.node.role) - """ - return "sh.node.role" diff --git a/src/gwproto/types/spaceheat_node_gt.py b/src/gwproto/types/spaceheat_node_gt.py index d639aa0f..6518d46a 100644 --- a/src/gwproto/types/spaceheat_node_gt.py +++ b/src/gwproto/types/spaceheat_node_gt.py @@ -2,28 +2,25 @@ from typing import Literal, Optional -from pydantic import BaseModel, ConfigDict, Field, StrictInt, model_validator +from pydantic import BaseModel, ConfigDict, StrictInt, model_validator from typing_extensions import Self -from gwproto.enums import ActorClass, Role -from gwproto.property_format import SpaceheatName, UUID4Str +from gwproto.enums import ActorClass +from gwproto.property_format import HandleName, SpaceheatName, UUID4Str class SpaceheatNodeGt(BaseModel): - ShNodeId: UUID4Str - Alias: SpaceheatName + Name: SpaceheatName + ActorHierarchyName: Optional[HandleName] = None + Handle: Optional[HandleName] = None ActorClass: ActorClass - Role: Role DisplayName: Optional[str] = None - ComponentId: Optional[UUID4Str] = None - ReportingSamplePeriodS: Optional[int] = None - InPowerMetering: Optional[bool] = Field( - title="InPowerMetering", - default=None, - ) + ComponentId: Optional[str] = None NameplatePowerW: Optional[StrictInt] = None + InPowerMetering: Optional[bool] = None + ShNodeId: UUID4Str TypeName: Literal["spaceheat.node.gt"] = "spaceheat.node.gt" - Version: Literal["120"] = "120" + Version: Literal["200"] = "200" @model_validator(mode="after") def check_axiom_1(self) -> Self: diff --git a/tests/config/hardware-layout.json b/tests/config/hardware-layout.json index ff712a07..ad66952e 100644 --- a/tests/config/hardware-layout.json +++ b/tests/config/hardware-layout.json @@ -467,34 +467,34 @@ ], "ShNodes": [ { - "Alias": "a", + "Name": "a", "Role": "Atn", "ActorClass": "Atn", "DisplayName": "AtomicTNode", "ShNodeId": "b354edeb-0c82-4e55-80cb-7ab669ac2ad9", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "home", + "Name": "home", "Role": "HomeAlone", "ActorClass": "HomeAlone", "DisplayName": "Little Orange House HomeAlone", "ShNodeId": "34470c9d-fa25-4077-909b-2f981a691d7e", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "s", + "Name": "s", "Role": "Scada", "ActorClass": "Scada", "DisplayName": "Little Orange House Main Scada", "ShNodeId": "259f9431-c6a1-4170-8766-04cbf65cff4a", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "elt1", + "Name": "elt1", "Role": "BoostElement", "ActorClass": "NoActor", "DisplayName": "First boost element", @@ -503,168 +503,152 @@ "InPowerMetering": true, "NameplatePowerW": 4500, "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "power-meter", + "Name": "power-meter", "Role": "PowerMeter", "ActorClass": "PowerMeter", "DisplayName": "Main Power Meter Little Orange House Test System", "ShNodeId": "0dd8a803-4724-4f49-b845-14ff57bdb3e6", "ComponentId": "2bfd0036-0b0e-4732-8790-bc7d0536a85e", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "buffer-cold-pipe", - "Role": "Unknown", + "Name": "buffer-cold-pipe", "ActorClass": "NoActor", "DisplayName": "Buffer Cold Pipe", "ShNodeId": "d2aa5af6-2cc7-4067-bd70-251c65b86a34", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "buffer-hot-pipe", - "Role": "Unknown", + "Name": "buffer-hot-pipe", "ActorClass": "NoActor", "DisplayName": "Buffer Hot Pipe", "ShNodeId": "d64e2fa3-5ca2-4665-b60a-253323455f5a", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "dist-rwt", - "Role": "Unknown", + "Name": "dist-rwt", "ActorClass": "NoActor", "DisplayName": "Dist Return Water Temp", "ShNodeId": "dc8a3e92-e09b-4976-bc32-c7f2dae22dc5", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "dist-swt", - "Role": "Unknown", + "Name": "dist-swt", "ActorClass": "NoActor", "DisplayName": "Dist Source Water Temp", "ShNodeId": "dfb1c311-f2db-4752-be11-ac0d4d91d71b", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "hp-ewt", - "Role": "Unknown", + "Name": "hp-ewt", "ActorClass": "NoActor", "DisplayName": "Heat Pump Entering Water Temp", "ShNodeId": "e76073f4-67ae-4324-b07f-0c9add733de7", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "hp-lwt", - "Role": "Unknown", + "Name": "hp-lwt", "ActorClass": "NoActor", "DisplayName": "Heat Pump Leaving Water Temp", "ShNodeId": "1fe2a01f-d60a-4362-bc5e-949ddfcd5003", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "oat", - "Role": "Unknown", + "Name": "oat", "ActorClass": "NoActor", "DisplayName": "Store Cold Pipe", "ShNodeId": "7ef9d1af-5f6c-430e-9d03-c00e480fcd9a", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "store-cold-pipe", - "Role": "Unknown", + "Name": "store-cold-pipe", "ActorClass": "NoActor", "DisplayName": "Store Cold Pipe", "ShNodeId": "e0e9b848-a593-465d-b9df-9d42c16cfffa", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "store-hot-pipe", - "Role": "Unknown", + "Name": "store-hot-pipe", "ActorClass": "NoActor", "DisplayName": "Store Hot Pipe", "ShNodeId": "26b31718-feec-446e-ba1e-c921d760f1dd", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "store-pump", - "Role": "Unknown", + "Name": "store-pump", "ActorClass": "NoActor", "DisplayName": "Store Pump", "ShNodeId": "c35d9c77-1ebb-4822-b01e-00ff189092f7", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "hp-idu", - "Role": "Unknown", + "Name": "hp-idu", "ActorClass": "NoActor", "DisplayName": "HP IDU", "InPowerMetering": true, "NameplatePowerW": 3500, "ShNodeId": "7da56e55-3cf0-4a2f-9c06-0c6c176b795c", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "hp-odu", - "Role": "Unknown", + "Name": "hp-odu", "ActorClass": "NoActor", "DisplayName": "HP ODU", "InPowerMetering": true, "NameplatePowerW": 6500, "ShNodeId": "f1692ea8-9866-423c-b260-93d2affc9881", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "zone1-down", - "Role": "Unknown", + "Name": "zone1-down", "ActorClass": "NoActor", "DisplayName": "Down Zone", "ShNodeId": "be8df9e8-1a72-4574-9575-dca70dbec214", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "analog-temp", - "Role": "Unknown", + "Name": "analog-temp", "ActorClass": "MultipurposeSensor", "DisplayName": "GridWorks MultiTemp", "ShNodeId": "8bfc40f1-7c84-4e88-9214-78c304730ae3", "ComponentId": "dfbb257e-a851-437b-b9af-55f948f7d4af", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { - "Alias": "zone1-down-stat", - "Role": "Unknown", + "Name": "zone1-down-stat", "ActorClass": "HoneywellThermostat", "DisplayName": "Zone 1 (Down) Thermostat", "ComponentId": "2c302eed-2f86-4ed6-8019-df31c22e1704", "ShNodeId": "8c9c155c-5b9a-448c-8df3-2edd9f4ebdb7", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" }, { "ActorClass": "Hubitat", - "Alias": "hubitat", + "Name": "hubitat", "ComponentId": "ab7bc99c-08ed-489e-b760-e36b57653e7e", "DisplayName": "Hubitat 81:15:21", - "Role": "Unknown", "ShNodeId": "13d9b3cb-2b13-444c-9e03-f31203772b64", "TypeName": "spaceheat.node.gt", - "Version": "120" + "Version": "200" } ], "DataChannels": [ diff --git a/tests/types/test_spaceheat_node_gt.py b/tests/types/test_spaceheat_node_gt.py index 4b56d6d7..a36250ca 100644 --- a/tests/types/test_spaceheat_node_gt.py +++ b/tests/types/test_spaceheat_node_gt.py @@ -9,15 +9,14 @@ def test_spaceheat_node_gt_generated() -> None: d = { "ShNodeId": "41f2ae73-8782-406d-bda7-a95b5abe317e", - "Alias": "elt1", + "Name": "elt1", "ActorClass": "NoActor", - "Role": "BoostElement", "DisplayName": "First boost element", "ComponentId": "80f95280-e999-49e0-a0e4-a7faf3b5b3bd", "ReportingSamplePeriodS": 300, "InPowerMetering": False, "TypeName": "spaceheat.node.gt", - "Version": "120", + "Version": "200", } assert SpaceheatNodeGt.model_validate(d).model_dump(exclude_none=True) == d From 6ad0aeb330eb3c288f26484cc9975f531b6e81b8 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Sun, 6 Oct 2024 13:22:49 -0400 Subject: [PATCH 166/168] status -> report --- src/gwproto/message.py | 29 +++------- src/gwproto/messages/__init__.py | 7 +-- src/gwproto/messages/event.py | 10 ++-- src/gwproto/types/__init__.py | 8 +++ src/gwproto/types/channel_readings.py | 28 ++++++++++ src/gwproto/types/report.py | 57 ++++++++++++++++++++ src/gwproto/types/single_reading.py | 20 +++++++ src/gwproto/types/snapshot_spaceheat.py | 21 +++++--- src/gwproto/types/synced_readings.py | 30 +++++++++++ tests/data/report_message.json | 58 ++++++++++++++++++++ tests/data/snapshot_message.json | 63 ++++++++-------------- tests/data/status_message.json | 58 -------------------- tests/test_decoders.py | 71 ++++++++++++++----------- tests/types/test_channel_readings.py | 18 +++++++ tests/types/test_report.py | 42 +++++++++++++++ tests/types/test_single_reading.py | 17 ++++++ tests/types/test_snapshot_spaceheat.py | 35 +++++++----- tests/types/test_synced_readings.py | 17 ++++++ 18 files changed, 410 insertions(+), 179 deletions(-) create mode 100644 src/gwproto/types/channel_readings.py create mode 100644 src/gwproto/types/report.py create mode 100644 src/gwproto/types/single_reading.py create mode 100644 src/gwproto/types/synced_readings.py create mode 100644 tests/data/report_message.json delete mode 100644 tests/data/status_message.json create mode 100644 tests/types/test_channel_readings.py create mode 100644 tests/types/test_report.py create mode 100644 tests/types/test_single_reading.py create mode 100644 tests/types/test_synced_readings.py diff --git a/src/gwproto/message.py b/src/gwproto/message.py index 414a6a8e..62554d01 100644 --- a/src/gwproto/message.py +++ b/src/gwproto/message.py @@ -1,16 +1,6 @@ # ruff: noqa: ANN401 -import typing -from typing import ( - Any, - Callable, - Generic, - Literal, - Mapping, - Optional, - TypeAlias, - TypeVar, - Union, -) + +from typing import Any, Callable, Generic, Literal, Mapping, Optional, TypeVar, Union from pydantic import BaseModel @@ -41,25 +31,22 @@ class Header(BaseModel): PAYLOAD_TYPE_FIELDS = ["TypeName", "type_alias", "TypeName", "type_name"] -GRIDWORKS_ENVELOPE_TYPE: str = "gw" +GRIDWORKS_ENVELOPE_TYPE = "gw" -def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict[str, Any]) -> None: +def ensure_arg(arg_name: str, default_value: Any, kwargs_dict: dict) -> None: if arg_name not in kwargs_dict: payload = kwargs_dict.get("Payload") if payload is None or not hasattr(payload, arg_name): kwargs_dict[arg_name] = default_value -HeaderAlias: TypeAlias = Header - - class Message(BaseModel, Generic[PayloadT]): Header: Header Payload: PayloadT - TypeName: Literal["gw"] = GRIDWORKS_ENVELOPE_TYPE # type: ignore[assignment] + TypeName: Literal["gw"] = GRIDWORKS_ENVELOPE_TYPE - def __init__(self, header: Optional[HeaderAlias] = None, **kwargs: Any) -> None: + def __init__(self, header: Optional[Header] = None, **kwargs: Any) -> None: if header is None: header = self._header_from_kwargs(kwargs) super().__init__(Header=header, **kwargs) @@ -72,13 +59,13 @@ def src(self) -> str: @classmethod def type_name(cls) -> str: - return typing.cast(str, Message.model_fields["TypeName"].default) + return Message.model_fields["TypeName"].default def mqtt_topic(self) -> str: return MQTTTopic.encode(self.type_name(), self.src(), self.message_type()) @classmethod - def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> HeaderAlias: + def _header_from_kwargs(cls, kwargs: dict[str, Any]) -> Header: header_kwargs = {} if "Payload" in kwargs: payload = kwargs["Payload"] diff --git a/src/gwproto/messages/__init__.py b/src/gwproto/messages/__init__.py index 177518c1..9b63b972 100644 --- a/src/gwproto/messages/__init__.py +++ b/src/gwproto/messages/__init__.py @@ -8,6 +8,8 @@ __all__ = [ "Ack", "AnyEvent", + "Report", + "ChannelReadings", "CommEvent", "EventBase", "EventMessage", @@ -15,8 +17,6 @@ "GtShCliAtnCmd", "GtShMultipurposeTelemetryStatus", "GtShSimpleTelemetryStatus", - "GtShStatus", - "GtShStatusEvent", "GtShTelemetryFromMultipurposeSensor", "MQTTConnectEvent", "MQTTConnectFailedEvent", @@ -29,9 +29,10 @@ "ProblemEvent", "Problems", "ResponseTimeoutEvent", + "SingleReading", "ShutdownEvent", "SnapshotSpaceheat", "SnapshotSpaceheatEvent", - "StartupEvent", + "SyncedReadings" "StartupEvent", "TelemetrySnapshotSpaceheat", ] diff --git a/src/gwproto/messages/event.py b/src/gwproto/messages/event.py index 09471651..563d0369 100644 --- a/src/gwproto/messages/event.py +++ b/src/gwproto/messages/event.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, Field, field_validator from gwproto.message import Message, as_enum -from gwproto.types import GtShStatus, SnapshotSpaceheat +from gwproto.types import Report, SnapshotSpaceheat class EventBase(BaseModel): @@ -100,13 +100,13 @@ class PeerActiveEvent(CommEvent): ) -class GtShStatusEvent(EventBase): - status: GtShStatus - TypeName: Literal["gridworks.event.gt.sh.status"] = "gridworks.event.gt.sh.status" +class ReportEvent(EventBase): + Report: Report + TypeName: Literal["gridworks.event.report"] = "gridworks.event.report" class SnapshotSpaceheatEvent(EventBase): - snap: SnapshotSpaceheat + Snap: SnapshotSpaceheat TypeName: Literal["gridworks.event.snapshot.spaceheat"] = ( "gridworks.event.snapshot.spaceheat" ) diff --git a/src/gwproto/types/__init__.py b/src/gwproto/types/__init__.py index b722780b..de7d79cb 100644 --- a/src/gwproto/types/__init__.py +++ b/src/gwproto/types/__init__.py @@ -6,6 +6,7 @@ AdsChannelConfig, ) from gwproto.types.channel_config import ChannelConfig +from gwproto.types.channel_readings import ChannelReadings from gwproto.types.component_attribute_class_gt import ComponentAttributeClassGt from gwproto.types.component_gt import ComponentGt from gwproto.types.data_channel_gt import DataChannelGt @@ -43,6 +44,7 @@ HubitatTankComponentGt, ) from gwproto.types.power_watts import PowerWatts +from gwproto.types.report import Report from gwproto.types.resistive_heater_cac_gt import ( ResistiveHeaterCacGt, ) @@ -52,8 +54,10 @@ from gwproto.types.rest_poller_component_gt import ( RESTPollerComponentGt, ) +from gwproto.types.single_reading import SingleReading from gwproto.types.snapshot_spaceheat import SnapshotSpaceheat from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt +from gwproto.types.synced_readings import SyncedReadings from gwproto.types.telemetry_snapshot_spaceheat import ( TelemetrySnapshotSpaceheat, ) @@ -62,6 +66,7 @@ "Ads111xBasedCacGt", "Ads111xBasedComponentGt", "ChannelConfig", + "ChannelReadings", "ComponentAttributeClassGt", "ComponentGt", "DataChannelGt", @@ -81,9 +86,12 @@ "HubitatPollerComponentGt", "HubitatTankComponentGt", "PowerWatts", + "Report", "RESTPollerComponentGt", "ResistiveHeaterCacGt", "ResistiveHeaterComponentGt", + "SingleReading", + "SyncedReadings", "SnapshotSpaceheat", "SpaceheatNodeGt", "TelemetrySnapshotSpaceheat", diff --git a/src/gwproto/types/channel_readings.py b/src/gwproto/types/channel_readings.py new file mode 100644 index 00000000..6638df5f --- /dev/null +++ b/src/gwproto/types/channel_readings.py @@ -0,0 +1,28 @@ +"""Type channel.readings, version 000""" + +from typing import List, Literal + +from pydantic import BaseModel, ConfigDict, StrictInt, model_validator +from typing_extensions import Self + +from gwproto.property_format import SpaceheatName, UTCMilliseconds, UUID4Str + + +class ChannelReadings(BaseModel): + ChannelName: SpaceheatName + ChannelId: UUID4Str + ValueList: List[StrictInt] + ScadaReadTimeUnixMsList: List[UTCMilliseconds] + TypeName: Literal["channel.readings"] = "channel.readings" + Version: Literal["001"] = "001" + + model_config = ConfigDict(extra="allow", use_enum_values=True) + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: ListLengthConsistency. + ValueList and ScadaReadTimeUnixMsList must have the same length. + """ + # Implement check for axiom 1" + return self diff --git a/src/gwproto/types/report.py b/src/gwproto/types/report.py new file mode 100644 index 00000000..f4b10b8e --- /dev/null +++ b/src/gwproto/types/report.py @@ -0,0 +1,57 @@ +"""Type report, version 000""" + +from typing import List, Literal + +from pydantic import BaseModel, ConfigDict, PositiveInt, model_validator +from typing_extensions import Self + +from gwproto.property_format import ( + LeftRightDotStr, + UTCMilliseconds, + UTCSeconds, + UUID4Str, +) +from gwproto.types.channel_readings import ChannelReadings + + +class Report(BaseModel): + FromGNodeAlias: LeftRightDotStr + FromGNodeInstanceId: UUID4Str + AboutGNodeAlias: LeftRightDotStr + SlotStartUnixS: UTCSeconds + BatchedTransmissionPeriodS: PositiveInt + ChannelReadingList: List[ChannelReadings] + MessageCreatedMs: UTCMilliseconds + Id: UUID4Str + TypeName: Literal["report"] = "report" + Version: Literal["000"] = "000" + + model_config = ConfigDict(extra="allow", use_enum_values=True) + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: Time Consistency. + For every ScadaReadTimeUnixMs let read_s = read_ms / 1000. Let start_s be SlotStartUnixS. Then read_s >= start_s and start_s + BatchedTransmissionPeriodS + 1 + start_s > read_s. + """ + # Implement check for axiom 1" + + return self + + @model_validator(mode="after") + def check_axiom_2(self) -> Self: + """ + Axiom 2: Unique Channel names and Ids + """ + + ids = [cr.ChannelId for cr in self.ChannelReadingList] + if len(ids) != len(set(ids)): + raise ValueError( + "Axiom 1 violated! ChannelReadingList ChannelIds must be unique" + ) + names = [cr.ChannelName for cr in self.ChannelReadingList] + if len(names) != len(set(names)): + raise ValueError( + "Axiom 1 violated! ChannelReadingList ChannelNames must be unique" + ) + return self diff --git a/src/gwproto/types/single_reading.py b/src/gwproto/types/single_reading.py new file mode 100644 index 00000000..ef39e349 --- /dev/null +++ b/src/gwproto/types/single_reading.py @@ -0,0 +1,20 @@ +"""Type single.reading, version 000""" + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, StrictInt + +from gwproto.property_format import ( + SpaceheatName, + UTCMilliseconds, +) + + +class SingleReading(BaseModel): + ChannelName: SpaceheatName + Value: StrictInt + ScadaReadTimeUnixMs: UTCMilliseconds + TypeName: Literal["single.reading"] = "single.reading" + Version: Literal["000"] = "000" + + model_config = ConfigDict(use_enum_values=True) diff --git a/src/gwproto/types/snapshot_spaceheat.py b/src/gwproto/types/snapshot_spaceheat.py index 3bbb3134..49cc50e3 100644 --- a/src/gwproto/types/snapshot_spaceheat.py +++ b/src/gwproto/types/snapshot_spaceheat.py @@ -1,18 +1,23 @@ -"""Type snapshot.spaceheat, version 000""" +"""Type snapshot.spaceheat, version 001""" -from typing import Literal +from typing import List, Literal -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict -from gwproto.property_format import LeftRightDotStr, UUID4Str -from gwproto.types.telemetry_snapshot_spaceheat import ( - TelemetrySnapshotSpaceheat, +from gwproto.property_format import ( + LeftRightDotStr, + UTCMilliseconds, + UUID4Str, ) +from gwproto.types.single_reading import SingleReading class SnapshotSpaceheat(BaseModel): FromGNodeAlias: LeftRightDotStr FromGNodeInstanceId: UUID4Str - Snapshot: TelemetrySnapshotSpaceheat + SnapshotTimeUnixMs: UTCMilliseconds + LatestReadingList: List[SingleReading] TypeName: Literal["snapshot.spaceheat"] = "snapshot.spaceheat" - Version: Literal["000"] = "000" + Version: Literal["001"] = "001" + + model_config = ConfigDict(use_enum_values=True) diff --git a/src/gwproto/types/synced_readings.py b/src/gwproto/types/synced_readings.py new file mode 100644 index 00000000..10dfa807 --- /dev/null +++ b/src/gwproto/types/synced_readings.py @@ -0,0 +1,30 @@ +"""Type synced.readings, version 000""" + +from typing import List, Literal + +from pydantic import BaseModel, ConfigDict, StrictInt, model_validator +from typing_extensions import Self + +from gwproto.property_format import ( + SpaceheatName, + UTCMilliseconds, +) + + +class SyncedReadings(BaseModel): + ChannelNameList: List[SpaceheatName] + ValueList: List[StrictInt] + ScadaReadTimeUnixMs: UTCMilliseconds + TypeName: Literal["synced.readings"] = "synced.readings" + Version: Literal["000"] = "000" + + model_config = ConfigDict(use_enum_values=True) + + @model_validator(mode="after") + def check_axiom_1(self) -> Self: + """ + Axiom 1: List Length Consistency. + len(ChannelNameList) = len(ValueList) + """ + # Implement check for axiom 1" + return self diff --git a/tests/data/report_message.json b/tests/data/report_message.json new file mode 100644 index 00000000..784f0dd6 --- /dev/null +++ b/tests/data/report_message.json @@ -0,0 +1,58 @@ +{ +"Header": { + "Src":"hw1.isone.me.versant.keene.beech.scada", + "Dst":"", + "MessageType":"gridworks.event.report", + "MessageId":"4dab57dd-8b4e-4ea4-90a3-d63df9eeb061", + "AckRequired":true, + "TypeName":"gridworks.header" + }, + "Payload": { + "MessageId":"4dab57dd-8b4e-4ea4-90a3-d63df9eeb061", + "TimeNS":1708518810017356000, + "Src":"hw1.isone.me.versant.keene.beech.scada", + "TypeName":"gridworks.event.report", + "Report": { + "FromGNodeAlias":"hw1.isone.me.versant.keene.beech.scada", + "FromGNodeInstanceId":"98542a17-3180-4f2a-a929-6023f0e7a106", + "AboutGNodeAlias":"hw1.isone.me.versant.keene.beech.ta", + "SlotStartUnixS":1708518780, + "BatchedTransmissionPeriodS":30, + "MessageCreatedMs":1708518810017, + "ChannelReadingList":[ + { + "ChannelName": "hp-odu-pwr", + "ChannelId": "498da855-bac5-47e9-b83a-a11e56a50e67", + "ValueList":[ + 26, + 96, + 196 + ], + "ScadaReadTimeUnixMsList":[ + 1708518800235, + 1708518808236, + 1708518809232 + ], + "TypeName":"channel.readings", + "Version":"001" + }, + { + "ChannelName": "dist-pump-pwr", + "ChannelId": "a2ebe9fa-05ba-4665-a6ba-dbc85aee530c", + "ValueList":[ + 14 + ], + "ScadaReadTimeUnixMsList":[ + 1708518800235 + ], + "TypeName":"channel.readings", + "Version":"001" + } + ], + "Id":"4dab57dd-8b4e-4ea4-90a3-d63df9eeb061", + "TypeName":"report", + "Version":"000" + } + }, + "TypeName": "gw" +} \ No newline at end of file diff --git a/tests/data/snapshot_message.json b/tests/data/snapshot_message.json index 14938403..0b567d71 100644 --- a/tests/data/snapshot_message.json +++ b/tests/data/snapshot_message.json @@ -8,48 +8,27 @@ "TypeName": "gridworks.header" }, "Payload": { - "FromGNodeAlias": "hw1.isone.ct.newhaven.orange1.ta.scada", - "FromGNodeInstanceId": "28817671-3899-4e24-a337-abcb8633e47a", - "Snapshot": { - "AboutNodeAliasList": [ - "a-elt1-relay", - "a-tank-out-pump-relay", - "a-tank-out-pump-baseboard1-fan-relay", - "a-tank-out-temp1", - "a-tank-out-far-temp1", - "a-tank-in-temp1", - "a-tank-temp0", - "a-garage-temp1", - "a-elt1", - "a-tank-out-temp2", - "a-tank-in-temp2", - "a-garage-temp2", - "a-tankoutside-temp2" - ], - "ValueList": [ - 0, 0, 0, 17812, 15250, 14312, 21812, 15062, 0, -44, -3626, -3015, 3041 - ], - "ReportTimeUnixMs": 1676592300011, - "TypeName": "telemetry.snapshot.spaceheat", - "Version": "000", - "TelemetryNameList": [ - "5a71d4b3", - "5a71d4b3", - "5a71d4b3", - "c89d0ba1", - "c89d0ba1", - "c89d0ba1", - "c89d0ba1", - "c89d0ba1", - "af39eec9", - "c89d0ba1", - "c89d0ba1", - "c89d0ba1", - "c89d0ba1" - ] - }, + "FromGNodeAlias": "d1.isone.ct.newhaven.rose.scada", + "FromGNodeInstanceId": "0384ef21-648b-4455-b917-58a1172d7fc1", + "SnapshotTimeUnixMs": 1726636445320, + "LatestReadingList": [ + { + "ScadaReadTimeUnixMs": 1726636440150, + "ChannelName": "hw-ewt", + "Value": 54000, + "TypeName": "single.reading", + "Version": "000" + }, + { + "ScadaReadTimeUnixMs": 1726636440243, + "ChannelName": "hw-ewt", + "Value": 65232, + "TypeName": "single.reading", + "Version": "000" + } + ], "TypeName": "snapshot.spaceheat", - "Version": "000" - }, + "Version": "001" +}, "TypeName": "gw" } diff --git a/tests/data/status_message.json b/tests/data/status_message.json deleted file mode 100644 index 025bf7af..00000000 --- a/tests/data/status_message.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "Header": { - "AckRequired": false, - "Dst": "", - "MessageId": "", - "MessageType": "gt.sh.status", - "Src": "hw1.isone.ct.newhaven.orange1.ta.scada", - "TypeName": "gridworks.header" - }, - "Payload": { - "AboutGNodeAlias": "hw1.isone.ct.newhaven.orange1.ta", - "BooleanactuatorCmdList": [], - "FromGNodeAlias": "hw1.isone.ct.newhaven.orange1.ta.scada", - "FromGNodeId": "28817671-3899-4e24-a337-abcb8633e47a", - "MultipurposeTelemetryList": [ - { - "AboutNodeAlias": "elt1", - "ReadTimeUnixMsList": [1676592055586], - "SensorNodeAlias": "power-meter", - "TelemetryName": "PowerW", - "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "101", - "ValueList": [0] - }, - { - "AboutNodeAlias": "store-hot-pipe", - "ReadTimeUnixMsList": [ - 1676592000232, 1676592060801, 1676592120037, 1676592180555, - 1676592240089 - ], - "SensorNodeAlias": "analog-temp", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "101", - "ValueList": [77, 361, -115, -9, -44] - }, - { - "AboutNodeAlias": "store-cold-pipe", - "ReadTimeUnixMsList": [ - 1676592000232, 1676592060801, 1676592120037, 1676592180555, - 1676592240089 - ], - "SensorNodeAlias": "analog-temp", - "TelemetryName": "WaterTempCTimes1000", - "TypeName": "gt.sh.multipurpose.telemetry.status", - "Version": "101", - "ValueList": [-3587, -3574, -3771, -3656, -3626] - } - ], - "ReportingPeriodS": 300, - "SimpleTelemetryList": [], - "SlotStartUnixS": 1676592000, - "StatusUid": "82abba64-d0df-4907-9b2b-6cb585421068", - "TypeName": "gt.sh.status", - "Version": "110" - }, - "TypeName": "gw" -} diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 0eb2db90..82a7a513 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -8,7 +8,6 @@ from gwproto.messages import ( Ack, AnyEvent, - GtShStatusEvent, MQTTConnectEvent, MQTTConnectFailedEvent, MQTTDisconnectEvent, @@ -17,6 +16,7 @@ PingMessage, ProblemEvent, Problems, + ReportEvent, ResponseTimeoutEvent, ShutdownEvent, SnapshotSpaceheatEvent, @@ -24,8 +24,8 @@ ) from gwproto.types import ( GtShCliAtnCmd, - GtShStatus, PowerWatts, + Report, SnapshotSpaceheat, ) from tests.dummy_decoders import CHILD, PARENT @@ -43,7 +43,7 @@ def get_stored_message_dicts() -> dict: def child_to_parent_payload_dicts() -> dict: d = {} - for prefix in ["status", "snapshot"]: + for prefix in ["report", "snapshot"]: with (TEST_DATA_DIR / f"{prefix}_message.json").open() as f: d[prefix] = json.loads(f.read()) d[prefix]["Header"]["Src"] = CHILD @@ -52,50 +52,42 @@ def child_to_parent_payload_dicts() -> dict: def child_to_parent_messages() -> list[MessageCase]: stored_message_dicts = child_to_parent_payload_dicts() - status_message_dict = stored_message_dicts["status"] - gt_sh_status = GtShStatus.model_validate(status_message_dict["Payload"]) - gt_sh_status_event = GtShStatusEvent(Src=CHILD, status=gt_sh_status) - unrecognized_status_event = AnyEvent(**gt_sh_status_event.model_dump()) - unrecognized_status_event.TypeName += ".foo" + report_event_dict = stored_message_dicts["report"] + report = Report.model_validate(report_event_dict["Payload"]["Report"]) + report_event = ReportEvent.model_validate(report_event_dict["Payload"]) + unrecongized_report_event = AnyEvent(**report_event.model_dump(exclude_none=True)) + unrecongized_report_event.TypeName += ".foo" unrecognized_event = AnyEvent( TypeName="gridworks.event.bar", MessageId="1", TimeNS=1, Src="1" ) unrecognizeable_not_event_type = AnyEvent( **dict( - gt_sh_status_event.model_dump(), + report_event.model_dump(), TypeName="bla", ) ) unrecognizeable_bad_event_content = {"TypeName": "gridworks.event.baz"} snap_message_dict = stored_message_dicts["snapshot"] snapshot_spaceheat = SnapshotSpaceheat.model_validate(snap_message_dict["Payload"]) - snapshot_event = SnapshotSpaceheatEvent(Src=CHILD, snap=snapshot_spaceheat) + snapshot_event = SnapshotSpaceheatEvent(Src=CHILD, Snap=snapshot_spaceheat) return [ - # PowerWatts MessageCase( - "PowerWatts", + "power-watts", Message(Src=CHILD, MessageType="power.watts", Payload=PowerWatts(Watts=1)), ), - # status - # QUESTION: why does this fail when replacing "gt.sh.status.110" with "gt.sh.status"? + # Batched Readings MessageCase( - "status-payload-obj", - Message(Src=CHILD, MessageType="gt.sh.status.110", Payload=gt_sh_status), + "report", + Message(Src=CHILD, MessageType="report", Payload=report), None, - gt_sh_status, + report, ), MessageCase( - "status-payload-dict", - Message(Src=CHILD, Payload=status_message_dict["Payload"]), + "report-as_dict", + Message(Src=CHILD, Payload=report), None, - gt_sh_status, - ), - MessageCase( - "status-payload-as_dict", - Message(Src=CHILD, Payload=gt_sh_status), - None, - gt_sh_status, + report, ), # snapshot MessageCase("snap", Message(**snap_message_dict), None, snapshot_spaceheat), @@ -111,11 +103,16 @@ def child_to_parent_messages() -> list[MessageCase]: None, snapshot_spaceheat, ), - # events - MessageCase("event-status", Message(Src=CHILD, Payload=gt_sh_status_event)), + # # events + MessageCase( + "br-event", + Message(Src=CHILD, Payload=report_event_dict["Payload"]), + None, + report_event, + ), MessageCase( "event-unrecognized-status", - Message(Src=CHILD, Payload=unrecognized_status_event), + Message(Src=CHILD, Payload=unrecongized_report_event), ), MessageCase( "event-unrecognized", Message(Src=CHILD, Payload=unrecognized_event) @@ -172,7 +169,7 @@ def child_to_parent_messages() -> list[MessageCase]: "peer-active-event", Message(Src=CHILD, Payload=PeerActiveEvent(PeerName=PARENT)), ), - # misc messages + # # misc messages MessageCase("ping", PingMessage(Src=CHILD)), MessageCase("ack", Message(Src=CHILD, Payload=Ack(AckMessageID="1"))), ] @@ -184,6 +181,14 @@ def parent_to_child_messages() -> list[MessageCase]: FromGNodeId=str(uuid.uuid4()), SendSnapshot=True, ) + # set_relay = GtDispatchBoolean( + # AboutNodeName="a.b.c", + # ToGNodeAlias="a.b.c", + # FromGNodeAlias="a.b.c", + # FromGNodeInstanceId=str(uuid.uuid4()), + # RelayState=True, + # SendTimeUnixMs=int(time.time() * 1000), + # ) return [ # misc messages MessageCase("ping", PingMessage(Src=PARENT)), @@ -194,6 +199,12 @@ def parent_to_child_messages() -> list[MessageCase]: None, snapshot_request, ), + # MessageCase( + # "set-relay", + # Message(Src=PARENT, Payload=set_relay), + # None, + # set_relay, + # ), ] diff --git a/tests/types/test_channel_readings.py b/tests/types/test_channel_readings.py new file mode 100644 index 00000000..e7b19122 --- /dev/null +++ b/tests/types/test_channel_readings.py @@ -0,0 +1,18 @@ +"""Tests channel.readings type, version 000""" + +from gwproto.types import ChannelReadings + + +def test_channel_readings_generated() -> None: + d = { + "ChannelName": "hp-odu-pwr", + "ChannelId": "498da855-bac5-47e9-b83a-a11e56a50e67", + "ValueList": [4559], + "ScadaReadTimeUnixMsList": [1656443705023], + "TypeName": "channel.readings", + "Version": "001", + } + + d2 = ChannelReadings.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d diff --git a/tests/types/test_report.py b/tests/types/test_report.py new file mode 100644 index 00000000..af85ba58 --- /dev/null +++ b/tests/types/test_report.py @@ -0,0 +1,42 @@ +"""Tests report type, version 000""" + +from gwproto.types import Report + + +def test_report_generated() -> None: + d = { + "FromGNodeAlias": "hw1.isone.me.versant.keene.beech.scada", + "FromGNodeInstanceId": "98542a17-3180-4f2a-a929-6023f0e7a106", + "AboutGNodeAlias": "hw1.isone.me.versant.keene.beech.ta", + "SlotStartUnixS": 1708518780, + "BatchedTransmissionPeriodS": 30, + "MessageCreatedMs": 1708518810017, + "ChannelReadingList": [ + { + "ChannelName": "hp-odu-pwr", + "ChannelId": "498da855-bac5-47e9-b83a-a11e56a50e67", + "ValueList": [26, 96, 196], + "ScadaReadTimeUnixMsList": [ + 1708518800235, + 1708518808236, + 1708518809232, + ], + "TypeName": "channel.readings", + "Version": "001", + }, + { + "ChannelName": "dist-pump-pwr", + "ChannelId": "a2ebe9fa-05ba-4665-a6ba-dbc85aee530c", + "ValueList": [14], + "ScadaReadTimeUnixMsList": [1708518800235], + "TypeName": "channel.readings", + "Version": "001", + }, + ], + "Id": "4dab57dd-8b4e-4ea4-90a3-d63df9eeb061", + "TypeName": "report", + "Version": "000", + } + + d2 = Report.model_validate(d).model_dump(exclude_none=True) + assert d2 == d diff --git a/tests/types/test_single_reading.py b/tests/types/test_single_reading.py new file mode 100644 index 00000000..3dec83ae --- /dev/null +++ b/tests/types/test_single_reading.py @@ -0,0 +1,17 @@ +"""Tests single.reading type, version 000""" + +from gwproto.types import SingleReading + + +def test_single_reading_generated() -> None: + d = { + "ScadaReadTimeUnixMs": 1656513094288, + "ChannelName": "hp-ewt", + "Value": 63430, + "TypeName": "single.reading", + "Version": "000", + } + + d2 = SingleReading.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d diff --git a/tests/types/test_snapshot_spaceheat.py b/tests/types/test_snapshot_spaceheat.py index 0174abae..0a935536 100644 --- a/tests/types/test_snapshot_spaceheat.py +++ b/tests/types/test_snapshot_spaceheat.py @@ -1,21 +1,32 @@ -"""Tests snapshot.spaceheat type, version 000""" +"""Tests snapshot.spaceheat type, version 001""" from gwproto.types import SnapshotSpaceheat def test_snapshot_spaceheat_generated() -> None: d = { - "FromGNodeAlias": "dwtest.isone.ct.newhaven.orange1.ta.scada", + "FromGNodeAlias": "d1.isone.ct.newhaven.rose.scada", "FromGNodeInstanceId": "0384ef21-648b-4455-b917-58a1172d7fc1", - "Snapshot": { - "TelemetryNameList": ["RelayState"], - "AboutNodeAliasList": ["a-elt1-relay"], - "ReportTimeUnixMs": 1656363448000, - "ValueList": [1], - "TypeName": "telemetry.snapshot.spaceheat", - "Version": "000", - }, + "SnapshotTimeUnixMs": 1726636445320, + "LatestReadingList": [ + { + "ScadaReadTimeUnixMs": 1726636440150, + "ChannelName": "hw-ewt", + "Value": 54000, + "TypeName": "single.reading", + "Version": "000", + }, + { + "ScadaReadTimeUnixMs": 1726636440243, + "ChannelName": "hw-ewt", + "Value": 65232, + "TypeName": "single.reading", + "Version": "000", + }, + ], "TypeName": "snapshot.spaceheat", - "Version": "000", + "Version": "001", } - assert SnapshotSpaceheat.model_validate(d).model_dump() == d + d2 = SnapshotSpaceheat.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d diff --git a/tests/types/test_synced_readings.py b/tests/types/test_synced_readings.py new file mode 100644 index 00000000..9b2f585e --- /dev/null +++ b/tests/types/test_synced_readings.py @@ -0,0 +1,17 @@ +"""Tests synced.readings type, version 000""" + +from gwproto.types import SyncedReadings + + +def test_synced_readings_generated() -> None: + d = { + "ScadaReadTimeUnixMs": 1656587343297, + "ChannelNameList": ["hp-ewt", "hp-lwt"], + "ValueList": [32755, 38870], + "TypeName": "synced.readings", + "Version": "000", + } + + d2 = SyncedReadings.model_validate(d).model_dump(exclude_none=True) + + assert d2 == d From 3dd697ca0f38186bc9a591be6b0b55809d29a9c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:42:38 +0000 Subject: [PATCH 167/168] build(deps): bump yarl from 1.11.1 to 1.13.1 Bumps [yarl](https://github.com/aio-libs/yarl) from 1.11.1 to 1.13.1. - [Release notes](https://github.com/aio-libs/yarl/releases) - [Changelog](https://github.com/aio-libs/yarl/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/yarl/compare/v1.11.1...v1.13.1) --- updated-dependencies: - dependency-name: yarl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 212 ++++++++++++++++++++++++++++------------------------ 1 file changed, 113 insertions(+), 99 deletions(-) diff --git a/poetry.lock b/poetry.lock index d5e8422d..7eb31715 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1412,37 +1412,51 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, @@ -2037,103 +2051,103 @@ tests-strict = ["pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)"] [[package]] name = "yarl" -version = "1.11.1" +version = "1.13.1" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, - {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, - {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, - {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, - {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, - {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, - {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, - {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, - {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, - {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, - {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, - {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, - {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, - {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, - {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, - {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, - {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, - {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, + {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:82e692fb325013a18a5b73a4fed5a1edaa7c58144dc67ad9ef3d604eccd451ad"}, + {file = "yarl-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df4e82e68f43a07735ae70a2d84c0353e58e20add20ec0af611f32cd5ba43fb4"}, + {file = "yarl-1.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec9dd328016d8d25702a24ee274932aebf6be9787ed1c28d021945d264235b3c"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5820bd4178e6a639b3ef1db8b18500a82ceab6d8b89309e121a6859f56585b05"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86c438ce920e089c8c2388c7dcc8ab30dfe13c09b8af3d306bcabb46a053d6f7"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3de86547c820e4f4da4606d1c8ab5765dd633189791f15247706a2eeabc783ae"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca53632007c69ddcdefe1e8cbc3920dd88825e618153795b57e6ebcc92e752a"}, + {file = "yarl-1.13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4ee1d240b84e2f213565f0ec08caef27a0e657d4c42859809155cf3a29d1735"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c49f3e379177f4477f929097f7ed4b0622a586b0aa40c07ac8c0f8e40659a1ac"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5c5e32fef09ce101fe14acd0f498232b5710effe13abac14cd95de9c274e689e"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab9524e45ee809a083338a749af3b53cc7efec458c3ad084361c1dbf7aaf82a2"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b1481c048fe787f65e34cb06f7d6824376d5d99f1231eae4778bbe5c3831076d"}, + {file = "yarl-1.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31497aefd68036d8e31bfbacef915826ca2e741dbb97a8d6c7eac66deda3b606"}, + {file = "yarl-1.13.1-cp310-cp310-win32.whl", hash = "sha256:1fa56f34b2236f5192cb5fceba7bbb09620e5337e0b6dfe2ea0ddbd19dd5b154"}, + {file = "yarl-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:1bbb418f46c7f7355084833051701b2301092e4611d9e392360c3ba2e3e69f88"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:216a6785f296169ed52cd7dcdc2612f82c20f8c9634bf7446327f50398732a51"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40c6e73c03a6befb85b72da213638b8aaa80fe4136ec8691560cf98b11b8ae6e"}, + {file = "yarl-1.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2430cf996113abe5aee387d39ee19529327205cda975d2b82c0e7e96e5fdabdc"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fb4134cc6e005b99fa29dbc86f1ea0a298440ab6b07c6b3ee09232a3b48f495"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309c104ecf67626c033845b860d31594a41343766a46fa58c3309c538a1e22b2"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f90575e9fe3aae2c1e686393a9689c724cd00045275407f71771ae5d690ccf38"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2e1626be8712333a9f71270366f4a132f476ffbe83b689dd6dc0d114796c74"}, + {file = "yarl-1.13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b66c87da3c6da8f8e8b648878903ca54589038a0b1e08dde2c86d9cd92d4ac9"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cf1ad338620249f8dd6d4b6a91a69d1f265387df3697ad5dc996305cf6c26fb2"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9915300fe5a0aa663c01363db37e4ae8e7c15996ebe2c6cce995e7033ff6457f"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:703b0f584fcf157ef87816a3c0ff868e8c9f3c370009a8b23b56255885528f10"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1d8e3ca29f643dd121f264a7c89f329f0fcb2e4461833f02de6e39fef80f89da"}, + {file = "yarl-1.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7055bbade838d68af73aea13f8c86588e4bcc00c2235b4b6d6edb0dbd174e246"}, + {file = "yarl-1.13.1-cp311-cp311-win32.whl", hash = "sha256:a3442c31c11088e462d44a644a454d48110f0588de830921fd201060ff19612a"}, + {file = "yarl-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:81bad32c8f8b5897c909bf3468bf601f1b855d12f53b6af0271963ee67fff0d2"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f452cc1436151387d3d50533523291d5f77c6bc7913c116eb985304abdbd9ec9"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9cec42a20eae8bebf81e9ce23fb0d0c729fc54cf00643eb251ce7c0215ad49fe"}, + {file = "yarl-1.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d959fe96e5c2712c1876d69af0507d98f0b0e8d81bee14cfb3f6737470205419"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8c837ab90c455f3ea8e68bee143472ee87828bff19ba19776e16ff961425b57"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94a993f976cdcb2dc1b855d8b89b792893220db8862d1a619efa7451817c836b"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b2442a415a5f4c55ced0fade7b72123210d579f7d950e0b5527fc598866e62c"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fdbf0418489525231723cdb6c79e7738b3cbacbaed2b750cb033e4ea208f220"}, + {file = "yarl-1.13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b7f6e699304717fdc265a7e1922561b02a93ceffdaefdc877acaf9b9f3080b8"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bcd5bf4132e6a8d3eb54b8d56885f3d3a38ecd7ecae8426ecf7d9673b270de43"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a93a4557f7fc74a38ca5a404abb443a242217b91cd0c4840b1ebedaad8919d4"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:22b739f99c7e4787922903f27a892744189482125cc7b95b747f04dd5c83aa9f"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2db874dd1d22d4c2c657807562411ffdfabec38ce4c5ce48b4c654be552759dc"}, + {file = "yarl-1.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4feaaa4742517eaceafcbe74595ed335a494c84634d33961214b278126ec1485"}, + {file = "yarl-1.13.1-cp312-cp312-win32.whl", hash = "sha256:bbf9c2a589be7414ac4a534d54e4517d03f1cbb142c0041191b729c2fa23f320"}, + {file = "yarl-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:d07b52c8c450f9366c34aa205754355e933922c79135125541daae6cbf31c799"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:95c6737f28069153c399d875317f226bbdea939fd48a6349a3b03da6829fb550"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cd66152561632ed4b2a9192e7f8e5a1d41e28f58120b4761622e0355f0fe034c"}, + {file = "yarl-1.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6a2acde25be0cf9be23a8f6cbd31734536a264723fca860af3ae5e89d771cd71"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18595e6a2ee0826bf7dfdee823b6ab55c9b70e8f80f8b77c37e694288f5de1"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a31d21089894942f7d9a8df166b495101b7258ff11ae0abec58e32daf8088813"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45f209fb4bbfe8630e3d2e2052535ca5b53d4ce2d2026bed4d0637b0416830da"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f722f30366474a99745533cc4015b1781ee54b08de73260b2bbe13316079851"}, + {file = "yarl-1.13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3bf60444269345d712838bb11cc4eadaf51ff1a364ae39ce87a5ca8ad3bb2c8"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:942c80a832a79c3707cca46bd12ab8aa58fddb34b1626d42b05aa8f0bcefc206"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:44b07e1690f010c3c01d353b5790ec73b2f59b4eae5b0000593199766b3f7a5c"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:396e59b8de7e4d59ff5507fb4322d2329865b909f29a7ed7ca37e63ade7f835c"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3bb83a0f12701c0b91112a11148b5217617982e1e466069d0555be9b372f2734"}, + {file = "yarl-1.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c92b89bffc660f1274779cb6fbb290ec1f90d6dfe14492523a0667f10170de26"}, + {file = "yarl-1.13.1-cp313-cp313-win32.whl", hash = "sha256:269c201bbc01d2cbba5b86997a1e0f73ba5e2f471cfa6e226bcaa7fd664b598d"}, + {file = "yarl-1.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:1d0828e17fa701b557c6eaed5edbd9098eb62d8838344486248489ff233998b8"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8be8cdfe20787e6a5fcbd010f8066227e2bb9058331a4eccddec6c0db2bb85b2"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08d7148ff11cb8e886d86dadbfd2e466a76d5dd38c7ea8ebd9b0e07946e76e4b"}, + {file = "yarl-1.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4afdf84610ca44dcffe8b6c22c68f309aff96be55f5ea2fa31c0c225d6b83e23"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0d12fe78dcf60efa205e9a63f395b5d343e801cf31e5e1dda0d2c1fb618073d"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298c1eecfd3257aa16c0cb0bdffb54411e3e831351cd69e6b0739be16b1bdaa8"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c14c16831b565707149c742d87a6203eb5597f4329278446d5c0ae7a1a43928e"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9bacedbb99685a75ad033fd4de37129449e69808e50e08034034c0bf063f99"}, + {file = "yarl-1.13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:658e8449b84b92a4373f99305de042b6bd0d19bf2080c093881e0516557474a5"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:373f16f38721c680316a6a00ae21cc178e3a8ef43c0227f88356a24c5193abd6"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:45d23c4668d4925688e2ea251b53f36a498e9ea860913ce43b52d9605d3d8177"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f7917697bcaa3bc3e83db91aa3a0e448bf5cde43c84b7fc1ae2427d2417c0224"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5989a38ba1281e43e4663931a53fbf356f78a0325251fd6af09dd03b1d676a09"}, + {file = "yarl-1.13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11b3ca8b42a024513adce810385fcabdd682772411d95bbbda3b9ed1a4257644"}, + {file = "yarl-1.13.1-cp38-cp38-win32.whl", hash = "sha256:dcaef817e13eafa547cdfdc5284fe77970b891f731266545aae08d6cce52161e"}, + {file = "yarl-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:7addd26594e588503bdef03908fc207206adac5bd90b6d4bc3e3cf33a829f57d"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0ae6637b173d0c40b9c1462e12a7a2000a71a3258fa88756a34c7d38926911c"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:576365c9f7469e1f6124d67b001639b77113cfd05e85ce0310f5f318fd02fe85"}, + {file = "yarl-1.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78f271722423b2d4851cf1f4fa1a1c4833a128d020062721ba35e1a87154a049"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d74f3c335cfe9c21ea78988e67f18eb9822f5d31f88b41aec3a1ec5ecd32da5"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1891d69a6ba16e89473909665cd355d783a8a31bc84720902c5911dbb6373465"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb382fd7b4377363cc9f13ba7c819c3c78ed97c36a82f16f3f92f108c787cbbf"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8854b9f80693d20cec797d8e48a848c2fb273eb6f2587b57763ccba3f3bd4b"}, + {file = "yarl-1.13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbf2c3f04ff50f16404ce70f822cdc59760e5e2d7965905f0e700270feb2bbfc"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fb9f59f3848edf186a76446eb8bcf4c900fe147cb756fbbd730ef43b2e67c6a7"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ef9b85fa1bc91c4db24407e7c4da93a5822a73dd4513d67b454ca7064e8dc6a3"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:098b870c18f1341786f290b4d699504e18f1cd050ed179af8123fd8232513424"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:8c723c91c94a3bc8033dd2696a0f53e5d5f8496186013167bddc3fb5d9df46a3"}, + {file = "yarl-1.13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44a4c40a6f84e4d5955b63462a0e2a988f8982fba245cf885ce3be7618f6aa7d"}, + {file = "yarl-1.13.1-cp39-cp39-win32.whl", hash = "sha256:84bbcdcf393139f0abc9f642bf03f00cac31010f3034faa03224a9ef0bb74323"}, + {file = "yarl-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:fc2931ac9ce9c61c9968989ec831d3a5e6fcaaff9474e7cfa8de80b7aff5a093"}, + {file = "yarl-1.13.1-py3-none-any.whl", hash = "sha256:6a5185ad722ab4dd52d5fb1f30dcc73282eb1ed494906a92d1a228d3f89607b0"}, + {file = "yarl-1.13.1.tar.gz", hash = "sha256:ec8cfe2295f3e5e44c51f57272afbd69414ae629ec7c6b27f5a410efc78b70a0"}, ] [package.dependencies] From cb5f89eca778d298d05b6ca4633ad844a0a20f69 Mon Sep 17 00:00:00 2001 From: Jessica Millar Date: Mon, 7 Oct 2024 07:44:49 -0400 Subject: [PATCH 168/168] new version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9b1c42ba..d43533c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gridworks-protocol" -version = "0.8.0" +version = "1.0.0" description = "Gridworks Protocol" authors = ["Jessica Millar "] license = "MIT"