diff --git a/haco/capabilities.py b/haco/capabilities.py index 2a09a9f..17b1d83 100644 --- a/haco/capabilities.py +++ b/haco/capabilities.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Type, ClassVar from pydantic import Field -from corio.iterator_tools import strip_none +from corio.iterator import strip_none from haco.base import Base from haco.topics import AnnounceTopicState, AnnounceTopicCommand diff --git a/haco/control.py b/haco/control.py index bce7d3e..009cd02 100644 --- a/haco/control.py +++ b/haco/control.py @@ -6,7 +6,7 @@ from pydantic import Field from corio import Constants from corio import Path -from corio.json_tools import to_json +from corio.jsn import to_json from haco.base import Base from haco.capabilities import Capability diff --git a/haco/obs.py b/haco/obs.py index 74ade85..b0b1d93 100644 --- a/haco/obs.py +++ b/haco/obs.py @@ -3,13 +3,13 @@ Observability and logging setup for the haco library. """ -from corio import logging, debug, Constants +from corio import logs, debug, Constants from haco.paths import paths debug.trace() -logger = logging.get_logger( +logger = logs.get_logger( name=paths.name_ns, stream=Constants.DEVELOPMENT, version=paths.metadata.version, diff --git a/haco/tests/test_pydantic_models.py b/haco/tests/test_pydantic_models.py new file mode 100644 index 0000000..1c33d4e --- /dev/null +++ b/haco/tests/test_pydantic_models.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from types import SimpleNamespace + +import pytest + +from corio import Path + +from haco.climate import Climate +from haco.device import Device +from haco.switch import Switch + + +@pytest.fixture +def client_stub(): + return SimpleNamespace( + topic=Path("root/client"), + will=SimpleNamespace(topic="root/status"), + ) + + +def test_switch_announce_payload_matches_expected(client_stub): + switch = Switch(name="Main", icon="power") + device = Device(name="Dev", controls=[switch]) + device.set_parent(client_stub) + + data = switch.get_announce() + assert data == { + "platform": "switch", + "name": "Main", + "icon": "mdi:power", + "unique_id": "dev-main", + "availability_topic": "root/status", + "state_topic": "root/client/dev/main/default/state", + "command_topic": "root/client/dev/main/default/command", + } + + +def test_climate_announce_payload_has_expected_ha_keys(client_stub): + climate = Climate(name="Cl") + device = Device(name="Dev", controls=[climate]) + device.set_parent(client_stub) + + data = climate.get_announce() + + expected_subset = { + "platform", + "name", + "unique_id", + "availability_topic", + "temperature_unit", + "temperature_min", + "temperature_max", + "mode_command_topic", + "mode_state_topic", + "temperature_command_topic", + "temperature_state_topic", + "power_command_topic", + "power_state_topic", + } + assert expected_subset.issubset(set(data)) + assert not any(key in data for key in {"capabilities", "subscriptions", "device", "parent"}) + assert not any(callable(value) for value in data.values()) + + +def test_model_dump_excludes_runtime_extra_attributes(): + switch = Switch(name="Main") + switch.dynamic_callback = lambda value: value + + dumped = switch.model_dump() + assert "dynamic_callback" not in dumped + + +def test_parent_wiring_sets_control_subscriptions(client_stub): + switch = Switch(name="Main") + device = Device(name="Dev", controls=[switch]) + device.set_parent(client_stub) + + assert switch.unique_id == "dev-main" + assert switch.availability_topic == "root/status" + assert device.subscriptions is not None + assert "root/client/dev/main/default/command" in device.subscriptions + handler = device.subscriptions["root/client/dev/main/default/command"] + assert hasattr(handler, "handle") diff --git a/haco/utils.py b/haco/utils.py index f9bb484..67dc687 100644 --- a/haco/utils.py +++ b/haco/utils.py @@ -1,8 +1,8 @@ import string from typing import Callable -from corio import json -from corio.datatype_tools import to_bool +from corio import jsn +from corio.datatype import to_bool CHARS_ALLOWED = string.ascii_lowercase + string.digits SEPS = '_- /' @@ -76,8 +76,8 @@ class ConvertersNumeric(Converters): Converters for numeric values. """ - command: Callable = json.from_json - state: Callable = json.to_json + command: Callable = jsn.from_json + state: Callable = jsn.to_json def get_prefix(io: str) -> str: """