Spaces:
Sleeping
Sleeping
| """Provide common mysensors fixtures.""" | |
| from __future__ import annotations | |
| from collections.abc import AsyncGenerator, Callable, Generator | |
| import json | |
| from typing import Any | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| from mysensors import BaseSyncGateway | |
| from mysensors.persistence import MySensorsJSONDecoder | |
| from mysensors.sensor import Sensor | |
| import pytest | |
| from homeassistant.components.device_tracker.legacy import Device | |
| from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN | |
| from homeassistant.components.mysensors.config_flow import DEFAULT_BAUD_RATE | |
| from homeassistant.components.mysensors.const import ( | |
| CONF_BAUD_RATE, | |
| CONF_DEVICE, | |
| CONF_GATEWAY_TYPE, | |
| CONF_GATEWAY_TYPE_SERIAL, | |
| CONF_VERSION, | |
| DOMAIN, | |
| ) | |
| from homeassistant.core import HomeAssistant | |
| from homeassistant.setup import async_setup_component | |
| from tests.common import MockConfigEntry, load_fixture | |
| def device_tracker_storage(mock_device_tracker_conf: list[Device]) -> list[Device]: | |
| """Mock out device tracker known devices storage.""" | |
| devices = mock_device_tracker_conf | |
| return devices | |
| def mock_mqtt_fixture(hass: HomeAssistant) -> None: | |
| """Mock the MQTT integration.""" | |
| hass.config.components.add(MQTT_DOMAIN) | |
| def is_serial_port_fixture() -> Generator[MagicMock, None, None]: | |
| """Patch the serial port check.""" | |
| with patch("homeassistant.components.mysensors.gateway.cv.isdevice") as is_device: | |
| is_device.side_effect = lambda device: device | |
| yield is_device | |
| def gateway_nodes_fixture() -> dict[int, Sensor]: | |
| """Return the gateway nodes dict.""" | |
| return {} | |
| async def serial_transport_fixture( | |
| gateway_nodes: dict[int, Sensor], | |
| is_serial_port: MagicMock, | |
| ) -> AsyncGenerator[dict[int, Sensor], None]: | |
| """Mock a serial transport.""" | |
| with patch( | |
| "mysensors.gateway_serial.AsyncTransport", autospec=True | |
| ) as transport_class, patch("mysensors.task.OTAFirmware", autospec=True), patch( | |
| "mysensors.task.load_fw", autospec=True | |
| ), patch( | |
| "mysensors.task.Persistence", autospec=True | |
| ) as persistence_class: | |
| persistence = persistence_class.return_value | |
| mock_gateway_features(persistence, transport_class, gateway_nodes) | |
| yield transport_class | |
| def mock_gateway_features( | |
| persistence: MagicMock, transport_class: MagicMock, nodes: dict[int, Sensor] | |
| ) -> None: | |
| """Mock the gateway features.""" | |
| async def mock_schedule_save_sensors() -> None: | |
| """Load nodes from via persistence.""" | |
| gateway = transport_class.call_args[0][0] | |
| gateway.sensors.update(nodes) | |
| persistence.schedule_save_sensors = AsyncMock( | |
| side_effect=mock_schedule_save_sensors | |
| ) | |
| # For some reason autospeccing does not recognize these methods. | |
| persistence.safe_load_sensors = MagicMock() | |
| persistence.save_sensors = MagicMock() | |
| async def mock_connect() -> None: | |
| """Mock the start method.""" | |
| transport.connect_task = MagicMock() | |
| gateway = transport_class.call_args[0][0] | |
| gateway.on_conn_made(gateway) | |
| transport = transport_class.return_value | |
| transport.connect_task = None | |
| transport.connect.side_effect = mock_connect | |
| def transport_fixture(serial_transport: MagicMock) -> MagicMock: | |
| """Return the default mocked transport.""" | |
| return serial_transport | |
| def transport_write(transport: MagicMock) -> MagicMock: | |
| """Return the transport mock that accepts string messages.""" | |
| return transport.return_value.send | |
| async def serial_entry_fixture(hass: HomeAssistant) -> MockConfigEntry: | |
| """Create a config entry for a serial gateway.""" | |
| entry = MockConfigEntry( | |
| domain=DOMAIN, | |
| data={ | |
| CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL, | |
| CONF_VERSION: "2.3", | |
| CONF_DEVICE: "/test/device", | |
| CONF_BAUD_RATE: DEFAULT_BAUD_RATE, | |
| }, | |
| ) | |
| return entry | |
| def config_entry_fixture(serial_entry: MockConfigEntry) -> MockConfigEntry: | |
| """Provide the config entry used for integration set up.""" | |
| return serial_entry | |
| async def integration_fixture( | |
| hass: HomeAssistant, transport: MagicMock, config_entry: MockConfigEntry | |
| ) -> AsyncGenerator[MockConfigEntry, None]: | |
| """Set up the mysensors integration with a config entry.""" | |
| config: dict[str, Any] = {} | |
| config_entry.add_to_hass(hass) | |
| with patch("homeassistant.components.mysensors.device.UPDATE_DELAY", new=0): | |
| await async_setup_component(hass, DOMAIN, config) | |
| await hass.async_block_till_done() | |
| yield config_entry | |
| def receive_message( | |
| transport: MagicMock, integration: MockConfigEntry | |
| ) -> Callable[[str], None]: | |
| """Receive a message for the gateway.""" | |
| def receive_message_callback(message_string: str) -> None: | |
| """Receive a message with the transport. | |
| The message_string parameter is a string in the MySensors message format. | |
| """ | |
| gateway = transport.call_args[0][0] | |
| # node_id;child_id;command;ack;type;payload\n | |
| gateway.logic(message_string) | |
| return receive_message_callback | |
| def gateway_fixture( | |
| transport: MagicMock, integration: MockConfigEntry | |
| ) -> BaseSyncGateway: | |
| """Return a setup gateway.""" | |
| return transport.call_args[0][0] | |
| def load_nodes_state(fixture_path: str) -> dict: | |
| """Load mysensors nodes fixture.""" | |
| return json.loads(load_fixture(fixture_path), cls=MySensorsJSONDecoder) | |
| def update_gateway_nodes( | |
| gateway_nodes: dict[int, Sensor], nodes: dict[int, Sensor] | |
| ) -> dict: | |
| """Update the gateway nodes.""" | |
| gateway_nodes.update(nodes) | |
| return nodes | |
| def gps_sensor_state_fixture() -> dict: | |
| """Load the gps sensor state.""" | |
| return load_nodes_state("mysensors/gps_sensor_state.json") | |
| def gps_sensor(gateway_nodes: dict[int, Sensor], gps_sensor_state: dict) -> Sensor: | |
| """Load the gps sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, gps_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def power_sensor_state_fixture() -> dict: | |
| """Load the power sensor state.""" | |
| return load_nodes_state("mysensors/power_sensor_state.json") | |
| def power_sensor(gateway_nodes: dict[int, Sensor], power_sensor_state: dict) -> Sensor: | |
| """Load the power sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, power_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def energy_sensor_state_fixture() -> dict: | |
| """Load the energy sensor state.""" | |
| return load_nodes_state("mysensors/energy_sensor_state.json") | |
| def energy_sensor( | |
| gateway_nodes: dict[int, Sensor], energy_sensor_state: dict | |
| ) -> Sensor: | |
| """Load the energy sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, energy_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def sound_sensor_state_fixture() -> dict: | |
| """Load the sound sensor state.""" | |
| return load_nodes_state("mysensors/sound_sensor_state.json") | |
| def sound_sensor(gateway_nodes: dict[int, Sensor], sound_sensor_state: dict) -> Sensor: | |
| """Load the sound sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, sound_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def distance_sensor_state_fixture() -> dict: | |
| """Load the distance sensor state.""" | |
| return load_nodes_state("mysensors/distance_sensor_state.json") | |
| def distance_sensor( | |
| gateway_nodes: dict[int, Sensor], distance_sensor_state: dict | |
| ) -> Sensor: | |
| """Load the distance sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, distance_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def temperature_sensor_state_fixture() -> dict: | |
| """Load the temperature sensor state.""" | |
| return load_nodes_state("mysensors/temperature_sensor_state.json") | |
| def temperature_sensor( | |
| gateway_nodes: dict[int, Sensor], temperature_sensor_state: dict | |
| ) -> Sensor: | |
| """Load the temperature sensor.""" | |
| nodes = update_gateway_nodes(gateway_nodes, temperature_sensor_state) | |
| node = nodes[1] | |
| return node | |
| def text_node_state_fixture() -> dict: | |
| """Load the text node state.""" | |
| return load_nodes_state("mysensors/text_node_state.json") | |
| def text_node(gateway_nodes: dict[int, Sensor], text_node_state: dict) -> Sensor: | |
| """Load the text child node.""" | |
| nodes = update_gateway_nodes(gateway_nodes, text_node_state) | |
| node = nodes[1] | |
| return node | |