core/tests/components/greeneye_monitor/common.py

253 lines
7.6 KiB
Python

"""Common helpers for greeneye_monitor tests."""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, MagicMock
from homeassistant.components.greeneye_monitor import (
CONF_CHANNELS,
CONF_COUNTED_QUANTITY,
CONF_COUNTED_QUANTITY_PER_PULSE,
CONF_MONITORS,
CONF_NET_METERING,
CONF_NUMBER,
CONF_PULSE_COUNTERS,
CONF_SERIAL_NUMBER,
CONF_TEMPERATURE_SENSORS,
CONF_TIME_UNIT,
CONF_VOLTAGE_SENSORS,
DOMAIN,
)
from homeassistant.const import (
CONF_NAME,
CONF_PORT,
CONF_SENSORS,
CONF_TEMPERATURE_UNIT,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
SINGLE_MONITOR_SERIAL_NUMBER = 110011
def make_single_monitor_config_with_sensors(sensors: dict[str, Any]) -> dict[str, Any]:
"""Wrap the given sensor config in the boilerplate for a single monitor with serial number SINGLE_MONITOR_SERIAL_NUMBER."""
return {
DOMAIN: {
CONF_PORT: 7513,
CONF_MONITORS: [
{
CONF_SERIAL_NUMBER: f"00{SINGLE_MONITOR_SERIAL_NUMBER}",
**sensors,
}
],
}
}
SINGLE_MONITOR_CONFIG_NO_SENSORS = make_single_monitor_config_with_sensors({})
SINGLE_MONITOR_CONFIG_PULSE_COUNTERS = make_single_monitor_config_with_sensors(
{
CONF_PULSE_COUNTERS: [
{
CONF_NUMBER: 1,
CONF_NAME: "pulse_a",
CONF_COUNTED_QUANTITY: "pulses",
CONF_COUNTED_QUANTITY_PER_PULSE: 1.0,
CONF_TIME_UNIT: "s",
},
{
CONF_NUMBER: 2,
CONF_NAME: "pulse_2",
CONF_COUNTED_QUANTITY: "gal",
CONF_COUNTED_QUANTITY_PER_PULSE: 0.5,
CONF_TIME_UNIT: "min",
},
{
CONF_NUMBER: 3,
CONF_NAME: "pulse_3",
CONF_COUNTED_QUANTITY: "gal",
CONF_COUNTED_QUANTITY_PER_PULSE: 0.5,
CONF_TIME_UNIT: "h",
},
{
CONF_NUMBER: 4,
CONF_NAME: "pulse_d",
CONF_COUNTED_QUANTITY: "pulses",
CONF_COUNTED_QUANTITY_PER_PULSE: 1.0,
CONF_TIME_UNIT: "s",
},
]
}
)
SINGLE_MONITOR_CONFIG_POWER_SENSORS = make_single_monitor_config_with_sensors(
{
CONF_CHANNELS: [
{
CONF_NUMBER: 1,
CONF_NAME: "channel 1",
},
{
CONF_NUMBER: 2,
CONF_NAME: "channel two",
CONF_NET_METERING: True,
},
]
}
)
SINGLE_MONITOR_CONFIG_TEMPERATURE_SENSORS = make_single_monitor_config_with_sensors(
{
CONF_TEMPERATURE_SENSORS: {
CONF_TEMPERATURE_UNIT: "F",
CONF_SENSORS: [
{CONF_NUMBER: 1, CONF_NAME: "temp_a"},
{CONF_NUMBER: 2, CONF_NAME: "temp_2"},
{CONF_NUMBER: 3, CONF_NAME: "temp_c"},
{CONF_NUMBER: 4, CONF_NAME: "temp_d"},
{CONF_NUMBER: 5, CONF_NAME: "temp_5"},
{CONF_NUMBER: 6, CONF_NAME: "temp_f"},
{CONF_NUMBER: 7, CONF_NAME: "temp_g"},
{CONF_NUMBER: 8, CONF_NAME: "temp_h"},
],
}
}
)
SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS = make_single_monitor_config_with_sensors(
{
CONF_VOLTAGE_SENSORS: [
{
CONF_NUMBER: 1,
CONF_NAME: "voltage 1",
},
]
}
)
MULTI_MONITOR_CONFIG = {
DOMAIN: {
CONF_PORT: 7513,
CONF_MONITORS: [
{
CONF_SERIAL_NUMBER: "00000001",
CONF_TEMPERATURE_SENSORS: {
CONF_TEMPERATURE_UNIT: "C",
CONF_SENSORS: [{CONF_NUMBER: 1, CONF_NAME: "unit_1_temp_1"}],
},
},
{
CONF_SERIAL_NUMBER: "00000002",
CONF_TEMPERATURE_SENSORS: {
CONF_TEMPERATURE_UNIT: "F",
CONF_SENSORS: [{CONF_NUMBER: 1, CONF_NAME: "unit_2_temp_1"}],
},
},
{
CONF_SERIAL_NUMBER: "00000003",
CONF_TEMPERATURE_SENSORS: {
CONF_TEMPERATURE_UNIT: "C",
CONF_SENSORS: [{CONF_NUMBER: 1, CONF_NAME: "unit_3_temp_1"}],
},
},
],
}
}
async def setup_greeneye_monitor_component_with_config(
hass: HomeAssistant, config: ConfigType
) -> bool:
"""Set up the greeneye_monitor component with the given config. Return True if successful, False otherwise."""
result = await async_setup_component(
hass,
DOMAIN,
config,
)
await hass.async_block_till_done()
return result
def mock_with_listeners() -> MagicMock:
"""Create a MagicMock with methods that follow the same pattern for working with listeners in the greeneye_monitor API."""
mock = MagicMock()
add_listeners(mock)
return mock
def async_mock_with_listeners() -> AsyncMock:
"""Create an AsyncMock with methods that follow the same pattern for working with listeners in the greeneye_monitor API."""
mock = AsyncMock()
add_listeners(mock)
return mock
def add_listeners(mock: MagicMock | AsyncMock) -> None:
"""Add add_listener and remove_listener methods to the given mock that behave like their counterparts on objects from the greeneye_monitor API, plus a notify_all_listeners method that calls all registered listeners."""
mock.listeners = []
mock.add_listener = mock.listeners.append
mock.remove_listener = mock.listeners.remove
def notify_all_listeners(*args):
for listener in list(mock.listeners):
listener(*args)
mock.notify_all_listeners = notify_all_listeners
def mock_pulse_counter() -> MagicMock:
"""Create a mock GreenEye Monitor pulse counter."""
pulse_counter = mock_with_listeners()
pulse_counter.pulses = 1000
pulse_counter.pulses_per_second = 10
return pulse_counter
def mock_temperature_sensor() -> MagicMock:
"""Create a mock GreenEye Monitor temperature sensor."""
temperature_sensor = mock_with_listeners()
temperature_sensor.temperature = 32.0
return temperature_sensor
def mock_voltage_sensor() -> MagicMock:
"""Create a mock GreenEye Monitor voltage sensor."""
voltage_sensor = mock_with_listeners()
voltage_sensor.voltage = 120.0
return voltage_sensor
def mock_channel() -> MagicMock:
"""Create a mock GreenEye Monitor CT channel."""
channel = mock_with_listeners()
channel.absolute_watt_seconds = 1000
channel.polarized_watt_seconds = -400
channel.watts = None
return channel
def mock_monitor(serial_number: int) -> MagicMock:
"""Create a mock GreenEye Monitor."""
monitor = mock_with_listeners()
monitor.serial_number = serial_number
monitor.voltage_sensor = mock_voltage_sensor()
monitor.pulse_counters = [mock_pulse_counter() for i in range(4)]
monitor.temperature_sensors = [mock_temperature_sensor() for i in range(8)]
monitor.channels = [mock_channel() for i in range(32)]
return monitor
async def connect_monitor(
hass: HomeAssistant, monitors: AsyncMock, serial_number: int
) -> MagicMock:
"""Simulate a monitor connecting to Home Assistant. Returns the mock monitor API object."""
monitor = mock_monitor(serial_number)
monitors.add_monitor(monitor)
await hass.async_block_till_done()
return monitor