Add tests to bring greeneye_monitor to 99% coverage (#58661)

* Bring greeneye_monitor to 99% coverage.

* Pass monitor into listeners on Monitors

* Updates for changes in `dev`, create mock monitor

* Remove logging left in after debugging

* Remove xfails now that #58764 has merged
This commit is contained in:
Jonathan Keljo 2021-11-11 04:20:16 -08:00 committed by GitHub
parent a29264518c
commit a079b4fd58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 694 additions and 6 deletions

View File

@ -399,8 +399,6 @@ omit =
homeassistant/components/google_travel_time/sensor.py homeassistant/components/google_travel_time/sensor.py
homeassistant/components/gpmdp/media_player.py homeassistant/components/gpmdp/media_player.py
homeassistant/components/gpsd/sensor.py homeassistant/components/gpsd/sensor.py
homeassistant/components/greeneye_monitor/*
homeassistant/components/greeneye_monitor/sensor.py
homeassistant/components/greenwave/light.py homeassistant/components/greenwave/light.py
homeassistant/components/group/notify.py homeassistant/components/group/notify.py
homeassistant/components/growatt_server/sensor.py homeassistant/components/growatt_server/sensor.py

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import logging import logging
from greeneye import Monitors import greeneye
import voluptuous as vol import voluptuous as vol
from homeassistant.const import ( from homeassistant.const import (
@ -123,7 +123,7 @@ CONFIG_SCHEMA = vol.Schema({DOMAIN: COMPONENT_SCHEMA}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the GreenEye Monitor component.""" """Set up the GreenEye Monitor component."""
monitors = Monitors() monitors = greeneye.Monitors()
hass.data[DATA_GREENEYE_MONITOR] = monitors hass.data[DATA_GREENEYE_MONITOR] = monitors
server_config = config[DOMAIN] server_config = config[DOMAIN]

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any, Generic, Optional, TypeVar, cast from typing import Any, Generic, Optional, TypeVar, cast
import greeneye import greeneye
from greeneye import Monitors
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.const import ( from homeassistant.const import (
@ -145,7 +144,7 @@ class GEMSensor(Generic[T], SensorEntity):
monitors = self.hass.data[DATA_GREENEYE_MONITOR] monitors = self.hass.data[DATA_GREENEYE_MONITOR]
monitors.remove_listener(self._on_new_monitor) monitors.remove_listener(self._on_new_monitor)
def _try_connect_to_monitor(self, monitors: Monitors) -> bool: def _try_connect_to_monitor(self, monitors: greeneye.Monitors) -> bool:
monitor = monitors.monitors.get(self._monitor_serial_number) monitor = monitors.monitors.get(self._monitor_serial_number)
if not monitor: if not monitor:
return False return False

View File

@ -463,6 +463,9 @@ googlemaps==2.5.1
# homeassistant.components.gree # homeassistant.components.gree
greeclimate==0.12.3 greeclimate==0.12.3
# homeassistant.components.greeneye_monitor
greeneye_monitor==2.1
# homeassistant.components.growatt_server # homeassistant.components.growatt_server
growattServer==1.1.0 growattServer==1.1.0

View File

@ -0,0 +1 @@
"""Tests for the GreenEye Monitor integration."""

View File

@ -0,0 +1,205 @@
"""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",
},
]
}
)
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_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 = 120.0
monitor.pulse_counters = [mock_pulse_counter() for i in range(0, 4)]
monitor.temperature_sensors = [mock_temperature_sensor() for i in range(0, 8)]
monitor.channels = [mock_channel() for i in range(0, 32)]
return monitor

View File

@ -0,0 +1,118 @@
"""Common fixtures for testing greeneye_monitor."""
from typing import Any, Dict
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from homeassistant.components.greeneye_monitor import DOMAIN
from homeassistant.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ELECTRIC_POTENTIAL_VOLT,
POWER_WATT,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_registry import (
RegistryEntry,
async_get as get_entity_registry,
)
from .common import add_listeners
def assert_sensor_state(
hass: HomeAssistant,
entity_id: str,
expected_state: str,
attributes: Dict[str, Any] = {},
) -> None:
"""Assert that the given entity has the expected state and at least the provided attributes."""
state = hass.states.get(entity_id)
assert state
actual_state = state.state
assert actual_state == expected_state
for (key, value) in attributes.items():
assert key in state.attributes
assert state.attributes[key] == value
def assert_temperature_sensor_registered(
hass: HomeAssistant,
serial_number: int,
number: int,
name: str,
):
"""Assert that a temperature sensor entity was registered properly."""
sensor = assert_sensor_registered(hass, serial_number, "temp", number, name)
assert sensor.device_class == DEVICE_CLASS_TEMPERATURE
def assert_pulse_counter_registered(
hass: HomeAssistant,
serial_number: int,
number: int,
name: str,
quantity: str,
per_time: str,
):
"""Assert that a pulse counter entity was registered properly."""
sensor = assert_sensor_registered(hass, serial_number, "pulse", number, name)
assert sensor.unit_of_measurement == f"{quantity}/{per_time}"
def assert_power_sensor_registered(
hass: HomeAssistant, serial_number: int, number: int, name: str
) -> None:
"""Assert that a power sensor entity was registered properly."""
sensor = assert_sensor_registered(hass, serial_number, "current", number, name)
assert sensor.unit_of_measurement == POWER_WATT
assert sensor.device_class == DEVICE_CLASS_POWER
def assert_voltage_sensor_registered(
hass: HomeAssistant, serial_number: int, number: int, name: str
) -> None:
"""Assert that a voltage sensor entity was registered properly."""
sensor = assert_sensor_registered(hass, serial_number, "volts", number, name)
assert sensor.unit_of_measurement == ELECTRIC_POTENTIAL_VOLT
assert sensor.device_class == DEVICE_CLASS_VOLTAGE
def assert_sensor_registered(
hass: HomeAssistant,
serial_number: int,
sensor_type: str,
number: int,
name: str,
) -> RegistryEntry:
"""Assert that a sensor entity of a given type was registered properly."""
registry = get_entity_registry(hass)
unique_id = f"{serial_number}-{sensor_type}-{number}"
entity_id = registry.async_get_entity_id("sensor", DOMAIN, unique_id)
assert entity_id is not None
sensor = registry.async_get(entity_id)
assert sensor
assert sensor.unique_id == unique_id
assert sensor.original_name == name
return sensor
@pytest.fixture
def monitors() -> AsyncMock:
"""Provide a mock greeneye.Monitors object that has listeners and can add new monitors."""
with patch("greeneye.Monitors", new=AsyncMock) as mock_monitors:
add_listeners(mock_monitors)
mock_monitors.monitors = {}
def add_monitor(monitor: MagicMock) -> None:
"""Add the given mock monitor as a monitor with the given serial number, notifying any listeners on the Monitors object."""
serial_number = monitor.serial_number
mock_monitors.monitors[serial_number] = monitor
mock_monitors.notify_all_listeners(monitor)
mock_monitors.add_monitor = add_monitor
yield mock_monitors

View File

@ -0,0 +1,199 @@
"""Tests for greeneye_monitor component initialization."""
from __future__ import annotations
from unittest.mock import AsyncMock
import pytest
from homeassistant.components.greeneye_monitor import (
CONF_MONITORS,
CONF_NUMBER,
CONF_SERIAL_NUMBER,
CONF_TEMPERATURE_SENSORS,
DOMAIN,
)
from homeassistant.const import (
CONF_NAME,
CONF_PORT,
CONF_SENSORS,
CONF_TEMPERATURE_UNIT,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .common import (
SINGLE_MONITOR_CONFIG_NO_SENSORS,
SINGLE_MONITOR_CONFIG_POWER_SENSORS,
SINGLE_MONITOR_CONFIG_PULSE_COUNTERS,
SINGLE_MONITOR_CONFIG_TEMPERATURE_SENSORS,
SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS,
SINGLE_MONITOR_SERIAL_NUMBER,
setup_greeneye_monitor_component_with_config,
)
from .conftest import (
assert_power_sensor_registered,
assert_pulse_counter_registered,
assert_temperature_sensor_registered,
assert_voltage_sensor_registered,
)
async def test_setup_fails_if_no_sensors_defined(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup fails if there are no sensors defined in the YAML."""
success = await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_NO_SENSORS
)
assert not success
@pytest.mark.xfail(reason="Currently failing. Will fix in subsequent PR.")
async def test_setup_succeeds_no_config(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup succeeds if there is no config present in the YAML."""
assert await async_setup_component(hass, DOMAIN, {})
async def test_setup_creates_temperature_entities(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup registers temperature sensors properly."""
assert await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_TEMPERATURE_SENSORS
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 1, "temp_a"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 2, "temp_2"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 3, "temp_c"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 4, "temp_d"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 5, "temp_5"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 6, "temp_f"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 7, "temp_g"
)
assert_temperature_sensor_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 8, "temp_h"
)
async def test_setup_creates_pulse_counter_entities(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup registers pulse counters properly."""
assert await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_PULSE_COUNTERS
)
assert_pulse_counter_registered(
hass,
SINGLE_MONITOR_SERIAL_NUMBER,
1,
"pulse_a",
"pulses",
"s",
)
assert_pulse_counter_registered(
hass, SINGLE_MONITOR_SERIAL_NUMBER, 2, "pulse_2", "gal", "min"
)
assert_pulse_counter_registered(
hass,
SINGLE_MONITOR_SERIAL_NUMBER,
3,
"pulse_3",
"gal",
"h",
)
assert_pulse_counter_registered(
hass,
SINGLE_MONITOR_SERIAL_NUMBER,
4,
"pulse_d",
"pulses",
"s",
)
async def test_setup_creates_power_sensor_entities(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup registers power sensors correctly."""
assert await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_POWER_SENSORS
)
assert_power_sensor_registered(hass, SINGLE_MONITOR_SERIAL_NUMBER, 1, "channel 1")
assert_power_sensor_registered(hass, SINGLE_MONITOR_SERIAL_NUMBER, 2, "channel two")
async def test_setup_creates_voltage_sensor_entities(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that component setup registers voltage sensors properly."""
assert await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
assert_voltage_sensor_registered(hass, SINGLE_MONITOR_SERIAL_NUMBER, 1, "voltage 1")
async def test_multi_monitor_config(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that component setup registers entities from multiple monitors correctly."""
assert await setup_greeneye_monitor_component_with_config(
hass,
{
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"}
],
},
},
],
}
},
)
assert_temperature_sensor_registered(hass, 1, 1, "unit_1_temp_1")
assert_temperature_sensor_registered(hass, 2, 1, "unit_2_temp_1")
async def test_setup_and_shutdown(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that the component can set up and shut down cleanly, closing the underlying server on shutdown."""
server = AsyncMock()
monitors.start_server = AsyncMock(return_value=server)
assert await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_POWER_SENSORS
)
await hass.async_stop()
assert server.close.called

View File

@ -0,0 +1,165 @@
"""Tests for greeneye_monitor sensors."""
from unittest.mock import AsyncMock, MagicMock
from homeassistant.components.greeneye_monitor.sensor import (
DATA_PULSES,
DATA_WATT_SECONDS,
)
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_registry import async_get as get_entity_registry
from .common import (
SINGLE_MONITOR_CONFIG_POWER_SENSORS,
SINGLE_MONITOR_CONFIG_PULSE_COUNTERS,
SINGLE_MONITOR_CONFIG_TEMPERATURE_SENSORS,
SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS,
SINGLE_MONITOR_SERIAL_NUMBER,
mock_monitor,
setup_greeneye_monitor_component_with_config,
)
from .conftest import assert_sensor_state
async def test_disable_sensor_before_monitor_connected(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that a sensor disabled before its monitor connected stops listening for new monitors."""
# The sensor base class handles connecting the monitor, so we test this with a single voltage sensor for ease
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
assert len(monitors.listeners) == 1
await disable_entity(hass, "sensor.voltage_1")
assert len(monitors.listeners) == 0 # Make sure we cleaned up the listener
async def test_updates_state_when_monitor_connected(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that a sensor updates its state when its monitor first connects."""
# The sensor base class handles updating the state on connection, so we test this with a single voltage sensor for ease
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
assert_sensor_state(hass, "sensor.voltage_1", STATE_UNKNOWN)
assert len(monitors.listeners) == 1
connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert len(monitors.listeners) == 0 # Make sure we cleaned up the listener
assert_sensor_state(hass, "sensor.voltage_1", "120.0")
async def test_disable_sensor_after_monitor_connected(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that a sensor disabled after its monitor connected stops listening for sensor changes."""
# The sensor base class handles connecting the monitor, so we test this with a single voltage sensor for ease
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
monitor = connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert len(monitor.listeners) == 1
await disable_entity(hass, "sensor.voltage_1")
assert len(monitor.listeners) == 0
async def test_updates_state_when_sensor_pushes(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that a sensor entity updates its state when the underlying sensor pushes an update."""
# The sensor base class handles triggering state updates, so we test this with a single voltage sensor for ease
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
monitor = connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert_sensor_state(hass, "sensor.voltage_1", "120.0")
monitor.voltage = 119.8
monitor.notify_all_listeners()
assert_sensor_state(hass, "sensor.voltage_1", "119.8")
async def test_power_sensor_initially_unknown(
hass: HomeAssistant, monitors: AsyncMock
) -> None:
"""Test that the power sensor can handle its initial state being unknown (since the GEM API needs at least two packets to arrive before it can compute watts)."""
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_POWER_SENSORS
)
connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert_sensor_state(
hass, "sensor.channel_1", STATE_UNKNOWN, {DATA_WATT_SECONDS: 1000}
)
# This sensor was configured with net metering on, so we should be taking the
# polarized value
assert_sensor_state(
hass, "sensor.channel_two", STATE_UNKNOWN, {DATA_WATT_SECONDS: -400}
)
async def test_power_sensor(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that a power sensor reports its values correctly, including handling net metering."""
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_POWER_SENSORS
)
monitor = connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
monitor.channels[0].watts = 120.0
monitor.channels[1].watts = 120.0
monitor.channels[0].notify_all_listeners()
monitor.channels[1].notify_all_listeners()
assert_sensor_state(hass, "sensor.channel_1", "120.0", {DATA_WATT_SECONDS: 1000})
# This sensor was configured with net metering on, so we should be taking the
# polarized value
assert_sensor_state(hass, "sensor.channel_two", "120.0", {DATA_WATT_SECONDS: -400})
async def test_pulse_counter(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that a pulse counter sensor reports its values properly, including calculating different units."""
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_PULSE_COUNTERS
)
connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert_sensor_state(hass, "sensor.pulse_a", "10.0", {DATA_PULSES: 1000})
# This counter was configured with each pulse meaning 0.5 gallons and
# wanting to show gallons per minute, so 10 pulses per second -> 300 gal/min
assert_sensor_state(hass, "sensor.pulse_2", "300.0", {DATA_PULSES: 1000})
# This counter was configured with each pulse meaning 0.5 gallons and
# wanting to show gallons per hour, so 10 pulses per second -> 18000 gal/hr
assert_sensor_state(hass, "sensor.pulse_3", "18000.0", {DATA_PULSES: 1000})
async def test_temperature_sensor(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that a temperature sensor reports its values properly, including proper handling of when its native unit is different from that configured in hass."""
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_TEMPERATURE_SENSORS
)
connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
# The config says that the sensor is reporting in Fahrenheit; if we set that up
# properly, HA will have converted that to Celsius by default.
assert_sensor_state(hass, "sensor.temp_a", "0.0")
async def test_voltage_sensor(hass: HomeAssistant, monitors: AsyncMock) -> None:
"""Test that a voltage sensor reports its values properly."""
await setup_greeneye_monitor_component_with_config(
hass, SINGLE_MONITOR_CONFIG_VOLTAGE_SENSORS
)
connect_monitor(monitors, SINGLE_MONITOR_SERIAL_NUMBER)
assert_sensor_state(hass, "sensor.voltage_1", "120.0")
def connect_monitor(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)
return monitor
async def disable_entity(hass: HomeAssistant, entity_id: str) -> None:
"""Disable the given entity."""
entity_registry = get_entity_registry(hass)
entity_registry.async_update_entity(entity_id, disabled_by="user")
await hass.async_block_till_done()