"""The tests for the utility_meter component.""" from __future__ import annotations from datetime import timedelta from unittest.mock import patch import pytest from homeassistant.components.select import ( ATTR_OPTION, DOMAIN as SELECT_DOMAIN, SERVICE_SELECT_OPTION, ) from homeassistant.components.utility_meter.const import DOMAIN, SERVICE_RESET import homeassistant.components.utility_meter.select as um_select import homeassistant.components.utility_meter.sensor as um_sensor from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_UNIT_OF_MEASUREMENT, CONF_PLATFORM, EVENT_HOMEASSISTANT_START, Platform, UnitOfEnergy, ) from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from tests.common import MockConfigEntry, mock_restore_cache async def test_restore_state(hass: HomeAssistant) -> None: """Test utility sensor restore state.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "tariffs": ["onpeak", "midpeak", "offpeak"], } } } mock_restore_cache( hass, [ State( "select.energy_bill", "midpeak", ), ], ) assert await async_setup_component(hass, DOMAIN, config) assert await async_setup_component(hass, Platform.SENSOR, config) await hass.async_block_till_done() # restore from cache state = hass.states.get("select.energy_bill") assert state.state == "midpeak" @pytest.mark.parametrize( "meter", ( ["select.energy_bill"], "select.energy_bill", ), ) async def test_services(hass: HomeAssistant, meter) -> None: """Test energy sensor reset service.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "cycle": "hourly", "tariffs": ["peak", "offpeak"], }, "energy_bill2": { "source": "sensor.energy", "cycle": "hourly", "tariffs": ["peak", "offpeak"], }, } } assert await async_setup_component(hass, DOMAIN, config) assert await async_setup_component(hass, Platform.SENSOR, config) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) entity_id = config[DOMAIN]["energy_bill"]["source"] hass.states.async_set( entity_id, 1, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR} ) await hass.async_block_till_done() now = dt_util.utcnow() + timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 3, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "2" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "0" # Change tariff data = {ATTR_ENTITY_ID: "select.energy_bill", ATTR_OPTION: "offpeak"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() now += timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 4, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "2" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "1" # Change tariff data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "wrong_tariff"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() # Inexisting tariff, ignoring assert hass.states.get("select.energy_bill").state != "wrong_tariff" data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "peak"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() now += timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 5, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "3" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "1" # Reset meters data = {ATTR_ENTITY_ID: meter} await hass.services.async_call(DOMAIN, SERVICE_RESET, data) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "0" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "0" # meanwhile energy_bill2_peak accumulated all kWh state = hass.states.get("sensor.energy_bill2_peak") assert state.state == "4" async def test_services_config_entry(hass: HomeAssistant) -> None: """Test energy sensor reset service.""" config_entry = MockConfigEntry( data={}, domain=DOMAIN, options={ "cycle": "monthly", "delta_values": False, "name": "Energy bill", "net_consumption": False, "offset": 0, "source": "sensor.energy", "tariffs": ["peak", "offpeak"], }, title="Energy bill", ) config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) config_entry = MockConfigEntry( data={}, domain=DOMAIN, options={ "cycle": "monthly", "delta_values": False, "name": "Energy bill2", "net_consumption": False, "offset": 0, "source": "sensor.energy", "tariffs": ["peak", "offpeak"], }, title="Energy bill2", ) config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) entity_id = "sensor.energy" hass.states.async_set( entity_id, 1, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR} ) await hass.async_block_till_done() now = dt_util.utcnow() + timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 3, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "2" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "0" # Change tariff data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "offpeak"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() now += timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 4, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "2" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "1" # Change tariff data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "wrong_tariff"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() # Inexisting tariff, ignoring assert hass.states.get("select.energy_bill").state != "wrong_tariff" data = {ATTR_ENTITY_ID: "select.energy_bill", "option": "peak"} await hass.services.async_call(SELECT_DOMAIN, SERVICE_SELECT_OPTION, data) await hass.async_block_till_done() now += timedelta(seconds=10) with patch("homeassistant.util.dt.utcnow", return_value=now): hass.states.async_set( entity_id, 5, {ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR}, force_update=True, ) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "3" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "1" # Reset meters data = {ATTR_ENTITY_ID: "select.energy_bill"} await hass.services.async_call(DOMAIN, SERVICE_RESET, data) await hass.async_block_till_done() state = hass.states.get("sensor.energy_bill_peak") assert state.state == "0" state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "0" # meanwhile energy_bill2_peak accumulated all kWh state = hass.states.get("sensor.energy_bill2_peak") assert state.state == "4" async def test_cron(hass: HomeAssistant) -> None: """Test cron pattern.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "cron": "*/5 * * * *", } } } assert await async_setup_component(hass, DOMAIN, config) async def test_cron_and_meter(hass: HomeAssistant) -> None: """Test cron pattern and meter type fails.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "cycle": "hourly", "cron": "0 0 1 * *", } } } assert not await async_setup_component(hass, DOMAIN, config) async def test_both_cron_and_meter(hass: HomeAssistant) -> None: """Test cron pattern and meter type passes in different meter.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "cron": "0 0 1 * *", }, "water_bill": { "source": "sensor.water", "cycle": "hourly", }, } } assert await async_setup_component(hass, DOMAIN, config) async def test_cron_and_offset(hass: HomeAssistant) -> None: """Test cron pattern and offset fails.""" config = { "utility_meter": { "energy_bill": { "source": "sensor.energy", "offset": {"days": 1}, "cron": "0 0 1 * *", } } } assert not await async_setup_component(hass, DOMAIN, config) async def test_bad_cron(hass: HomeAssistant) -> None: """Test bad cron pattern.""" config = { "utility_meter": {"energy_bill": {"source": "sensor.energy", "cron": "*"}} } assert not await async_setup_component(hass, DOMAIN, config) async def test_setup_missing_discovery(hass: HomeAssistant) -> None: """Test setup with configuration missing discovery_info.""" assert not await um_select.async_setup_platform(hass, {CONF_PLATFORM: DOMAIN}, None) assert not await um_sensor.async_setup_platform(hass, {CONF_PLATFORM: DOMAIN}, None) @pytest.mark.parametrize( ("tariffs", "expected_entities"), ( ( [], ["sensor.electricity_meter"], ), ( ["high", "low"], [ "sensor.electricity_meter_low", "sensor.electricity_meter_high", "select.electricity_meter", ], ), ), ) async def test_setup_and_remove_config_entry( hass: HomeAssistant, tariffs: str, expected_entities: list[str] ) -> None: """Test setting up and removing a config entry.""" input_sensor_entity_id = "sensor.input" registry = er.async_get(hass) # Setup the config entry config_entry = MockConfigEntry( data={}, domain=DOMAIN, options={ "cycle": "monthly", "delta_values": False, "name": "Electricity meter", "net_consumption": False, "offset": 0, "source": input_sensor_entity_id, "tariffs": tariffs, }, title="Electricity meter", ) config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() assert len(hass.states.async_all()) == len(expected_entities) assert len(registry.entities) == len(expected_entities) for entity in expected_entities: assert hass.states.get(entity) assert entity in registry.entities # Remove the config entry assert await hass.config_entries.async_remove(config_entry.entry_id) await hass.async_block_till_done() # Check the state and entity registry entry are removed assert len(hass.states.async_all()) == 0 assert len(registry.entities) == 0