Add coordinators to Sense (#129171)

pull/129185/head
Keilin Bickar 2024-10-25 14:45:55 -04:00 committed by GitHub
parent bb36dd3893
commit 68284bed74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 194 additions and 242 deletions

View File

@ -1,10 +1,8 @@
"""Support for monitoring a Sense energy sensor."""
from dataclasses import dataclass
from datetime import timedelta
from functools import partial
import logging
from typing import Any
from sense_energy import (
ASyncSenseable,
@ -13,26 +11,18 @@ from sense_energy import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_EMAIL,
CONF_TIMEOUT,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import CONF_TIMEOUT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
ACTIVE_UPDATE_RATE,
SENSE_CONNECT_EXCEPTIONS,
SENSE_DEVICE_UPDATE,
SENSE_TIMEOUT_EXCEPTIONS,
SENSE_WEBSOCKET_EXCEPTIONS,
)
from .coordinator import SenseRealtimeCoordinator, SenseTrendCoordinator
_LOGGER = logging.getLogger(__name__)
@ -45,14 +35,14 @@ class SenseData:
"""Sense data type."""
data: ASyncSenseable
trends: DataUpdateCoordinator[Any]
trends: SenseTrendCoordinator
rt: SenseRealtimeCoordinator
async def async_setup_entry(hass: HomeAssistant, entry: SenseConfigEntry) -> bool:
"""Set up Sense from a config entry."""
entry_data = entry.data
email = entry_data[CONF_EMAIL]
timeout = entry_data[CONF_TIMEOUT]
access_token = entry_data.get("access_token", "")
@ -99,26 +89,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: SenseConfigEntry) -> boo
except SENSE_WEBSOCKET_EXCEPTIONS as err:
raise ConfigEntryNotReady(str(err) or "Error during realtime update") from err
async def _async_update_trend() -> None:
"""Update the trend data."""
try:
await gateway.update_trend_data()
except (SenseAuthenticationException, SenseMFARequiredException) as err:
_LOGGER.warning("Sense authentication expired")
raise ConfigEntryAuthFailed(err) from err
except SENSE_CONNECT_EXCEPTIONS as err:
raise UpdateFailed(err) from err
trends_coordinator: DataUpdateCoordinator[None] = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"Sense Trends {email}",
update_method=_async_update_trend,
update_interval=timedelta(seconds=300),
)
# Start out as unavailable so we do not report 0 data
# until the update happens
trends_coordinator.last_update_success = False
trends_coordinator = SenseTrendCoordinator(hass, gateway)
realtime_coordinator = SenseRealtimeCoordinator(hass, gateway)
# This can take longer than 60s and we already know
# sense is online since get_discovered_device_data was
@ -128,40 +100,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: SenseConfigEntry) -> boo
trends_coordinator.async_request_refresh(),
"sense.trends-coordinator-refresh",
)
entry.async_create_background_task(
hass,
realtime_coordinator.async_request_refresh(),
"sense.realtime-coordinator-refresh",
)
entry.runtime_data = SenseData(
data=gateway,
trends=trends_coordinator,
rt=realtime_coordinator,
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async def async_sense_update(_) -> None:
"""Retrieve latest state."""
try:
await gateway.update_realtime()
except SENSE_TIMEOUT_EXCEPTIONS as ex:
_LOGGER.error("Timeout retrieving data: %s", ex)
except SENSE_WEBSOCKET_EXCEPTIONS as ex:
_LOGGER.error("Failed to update data: %s", ex)
async_dispatcher_send(hass, f"{SENSE_DEVICE_UPDATE}-{gateway.sense_monitor_id}")
remove_update_callback = async_track_time_interval(
hass, async_sense_update, timedelta(seconds=ACTIVE_UPDATE_RATE)
)
@callback
def _remove_update_callback_at_stop(event) -> None:
remove_update_callback()
entry.async_on_unload(remove_update_callback)
entry.async_on_unload(
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, _remove_update_callback_at_stop
)
)
return True

View File

@ -8,13 +8,14 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import SenseConfigEntry
from .const import ATTRIBUTION, DOMAIN, MDI_ICONS, SENSE_DEVICE_UPDATE
from .const import ATTRIBUTION, DOMAIN, MDI_ICONS
from .coordinator import SenseRealtimeCoordinator
_LOGGER = logging.getLogger(__name__)
@ -26,8 +27,10 @@ async def async_setup_entry(
) -> None:
"""Set up the Sense binary sensor."""
sense_monitor_id = config_entry.runtime_data.data.sense_monitor_id
realtime_coordinator = config_entry.runtime_data.rt
devices = [
SenseBinarySensor(device, sense_monitor_id)
SenseBinarySensor(device, sense_monitor_id, realtime_coordinator)
for device in config_entry.runtime_data.data.devices
]
@ -41,19 +44,25 @@ def sense_to_mdi(sense_icon: str) -> str:
return f"mdi:{MDI_ICONS.get(sense_icon, "power-plug")}"
class SenseBinarySensor(BinarySensorEntity):
class SenseBinarySensor(
CoordinatorEntity[SenseRealtimeCoordinator], BinarySensorEntity
):
"""Implementation of a Sense energy device binary sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_available = False
_attr_device_class = BinarySensorDeviceClass.POWER
def __init__(self, device: SenseDevice, sense_monitor_id: str) -> None:
def __init__(
self,
device: SenseDevice,
sense_monitor_id: str,
coordinator: SenseRealtimeCoordinator,
) -> None:
"""Initialize the Sense binary sensor."""
super().__init__(coordinator)
self._attr_name = device.name
self._id = device.id
self._sense_monitor_id = sense_monitor_id
self._attr_unique_id = f"{sense_monitor_id}-{self._id}"
self._attr_icon = sense_to_mdi(device.icon)
self._device = device
@ -63,25 +72,10 @@ class SenseBinarySensor(BinarySensorEntity):
"""Return the old not so unique id of the binary sensor."""
return self._id
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
)
)
@callback
def _async_update_from_data(self) -> None:
"""Get the latest data, update state. Must not do I/O."""
new_state = self._device.is_on
if self._attr_available and self._attr_is_on == new_state:
return
self._attr_available = True
self._attr_is_on = new_state
self.async_write_ha_state()
@property
def is_on(self) -> bool:
"""Return the state of the sensor."""
return self._device.is_on
async def _migrate_old_unique_ids(

View File

@ -11,6 +11,7 @@ from sense_energy import (
DOMAIN = "sense"
DEFAULT_TIMEOUT = 30
ACTIVE_UPDATE_RATE = 60
TREND_UPDATE_RATE = 300
DEFAULT_NAME = "Sense"
SENSE_DEVICE_UPDATE = "sense_devices_update"

View File

@ -0,0 +1,76 @@
"""Sense Coordinators."""
from datetime import timedelta
import logging
from sense_energy import (
ASyncSenseable,
SenseAuthenticationException,
SenseMFARequiredException,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
ACTIVE_UPDATE_RATE,
SENSE_CONNECT_EXCEPTIONS,
SENSE_TIMEOUT_EXCEPTIONS,
SENSE_WEBSOCKET_EXCEPTIONS,
TREND_UPDATE_RATE,
)
_LOGGER = logging.getLogger(__name__)
class SenseCoordinator(DataUpdateCoordinator[None]):
"""Sense Trend Coordinator."""
def __init__(
self, hass: HomeAssistant, gateway: ASyncSenseable, name: str, update: int
) -> None:
"""Initialize."""
super().__init__(
hass,
logger=_LOGGER,
name=f"Sense {name} {gateway.sense_monitor_id}",
update_interval=timedelta(seconds=update),
)
self._gateway = gateway
self.last_update_success = False
class SenseTrendCoordinator(SenseCoordinator):
"""Sense Trend Coordinator."""
def __init__(self, hass: HomeAssistant, gateway: ASyncSenseable) -> None:
"""Initialize."""
super().__init__(hass, gateway, "Trends", TREND_UPDATE_RATE)
async def _async_update_data(self) -> None:
"""Update the trend data."""
try:
await self._gateway.update_trend_data()
except (SenseAuthenticationException, SenseMFARequiredException) as err:
_LOGGER.warning("Sense authentication expired")
raise ConfigEntryAuthFailed(err) from err
except SENSE_CONNECT_EXCEPTIONS as err:
raise UpdateFailed(err) from err
class SenseRealtimeCoordinator(SenseCoordinator):
"""Sense Realtime Coordinator."""
def __init__(self, hass: HomeAssistant, gateway: ASyncSenseable) -> None:
"""Initialize."""
super().__init__(hass, gateway, "Realtime", ACTIVE_UPDATE_RATE)
async def _async_update_data(self) -> None:
"""Retrieve latest state."""
try:
await self._gateway.update_realtime()
except SENSE_TIMEOUT_EXCEPTIONS as ex:
_LOGGER.error("Timeout retrieving data: %s", ex)
except SENSE_WEBSOCKET_EXCEPTIONS as ex:
_LOGGER.error("Failed to update data: %s", ex)

View File

@ -1,7 +1,6 @@
"""Support for monitoring a Sense energy sensor."""
from datetime import datetime
from typing import Any
from sense_energy import ASyncSenseable, Scale
from sense_energy.sense_api import SenseDevice
@ -17,14 +16,10 @@ from homeassistant.const import (
UnitOfEnergy,
UnitOfPower,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import SenseConfigEntry
from .const import (
@ -43,12 +38,16 @@ from .const import (
PRODUCTION_NAME,
PRODUCTION_PCT_ID,
PRODUCTION_PCT_NAME,
SENSE_DEVICE_UPDATE,
SOLAR_POWERED_ID,
SOLAR_POWERED_NAME,
TO_GRID_ID,
TO_GRID_NAME,
)
from .coordinator import (
SenseCoordinator,
SenseRealtimeCoordinator,
SenseTrendCoordinator,
)
# Sensor types/ranges
TRENDS_SENSOR_TYPES = {
@ -86,6 +85,7 @@ async def async_setup_entry(
"""Set up the Sense sensor."""
data = config_entry.runtime_data.data
trends_coordinator = config_entry.runtime_data.trends
realtime_coordinator = config_entry.runtime_data.rt
# Request only in case it takes longer
# than 60s
@ -94,22 +94,19 @@ async def async_setup_entry(
sense_monitor_id = data.sense_monitor_id
entities: list[SensorEntity] = [
SenseDevicePowerSensor(device, sense_monitor_id)
SenseDevicePowerSensor(device, sense_monitor_id, realtime_coordinator)
for device in config_entry.runtime_data.data.devices
]
for variant_id, variant_name in SENSOR_VARIANTS:
entities.append(
SensePowerSensor(
data,
sense_monitor_id,
variant_id,
variant_name,
data, sense_monitor_id, variant_id, variant_name, realtime_coordinator
)
)
entities.extend(
SenseVoltageSensor(data, i, sense_monitor_id)
SenseVoltageSensor(data, i, sense_monitor_id, realtime_coordinator)
for i in range(len(data.active_voltage))
)
@ -129,14 +126,28 @@ async def async_setup_entry(
async_add_entities(entities)
class SensePowerSensor(SensorEntity):
class SenseBaseSensor(CoordinatorEntity[SenseCoordinator], SensorEntity):
"""Base implementation of a Sense sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
def __init__(
self,
coordinator: SenseCoordinator,
sense_monitor_id: str,
unique_id: str,
) -> None:
"""Initialize the Sense sensor."""
super().__init__(coordinator)
self._attr_unique_id = f"{sense_monitor_id}-{unique_id}"
class SensePowerSensor(SenseBaseSensor):
"""Implementation of a Sense energy sensor."""
_attr_device_class = SensorDeviceClass.POWER
_attr_native_unit_of_measurement = UnitOfPower.WATT
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_available = False
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(
@ -145,106 +156,71 @@ class SensePowerSensor(SensorEntity):
sense_monitor_id: str,
variant_id: str,
variant_name: str,
realtime_coordinator: SenseRealtimeCoordinator,
) -> None:
"""Initialize the Sense sensor."""
self._attr_name = f"{ACTIVE_NAME} {variant_name}"
self._attr_unique_id = f"{sense_monitor_id}-{ACTIVE_TYPE}-{variant_id}"
self._data = data
self._sense_monitor_id = sense_monitor_id
self._variant_id = variant_id
self._variant_name = variant_name
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
)
super().__init__(
realtime_coordinator, sense_monitor_id, f"{ACTIVE_TYPE}-{variant_id}"
)
self._attr_name = f"{ACTIVE_NAME} {variant_name}"
self._data = data
self._variant_id = variant_id
@callback
def _async_update_from_data(self) -> None:
"""Update the sensor from the data. Must not do I/O."""
new_state = round(
@property
def native_value(self) -> float:
"""Return the state of the sensor."""
return round(
self._data.active_solar_power
if self._variant_id == PRODUCTION_ID
else self._data.active_power
)
if self._attr_available and self._attr_native_value == new_state:
return
self._attr_native_value = new_state
self._attr_available = True
self.async_write_ha_state()
class SenseVoltageSensor(SensorEntity):
class SenseVoltageSensor(SenseBaseSensor):
"""Implementation of a Sense energy voltage sensor."""
_attr_device_class = SensorDeviceClass.VOLTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_available = False
def __init__(
self,
data: ASyncSenseable,
index: int,
sense_monitor_id: str,
realtime_coordinator: SenseRealtimeCoordinator,
) -> None:
"""Initialize the Sense sensor."""
line_num = index + 1
self._attr_name = f"L{line_num} Voltage"
self._attr_unique_id = f"{sense_monitor_id}-L{line_num}"
super().__init__(realtime_coordinator, sense_monitor_id, f"L{index + 1}")
self._attr_name = f"L{index + 1} Voltage"
self._data = data
self._sense_monitor_id = sense_monitor_id
self._voltage_index = index
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
)
)
@callback
def _async_update_from_data(self) -> None:
"""Update the sensor from the data. Must not do I/O."""
new_state = round(self._data.active_voltage[self._voltage_index], 1)
if self._attr_available and self._attr_native_value == new_state:
return
self._attr_available = True
self._attr_native_value = new_state
self.async_write_ha_state()
@property
def native_value(self) -> float:
"""Return the state of the sensor."""
return round(self._data.active_voltage[self._voltage_index], 1)
class SenseTrendsSensor(CoordinatorEntity, SensorEntity):
class SenseTrendsSensor(SenseBaseSensor):
"""Implementation of a Sense energy sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
def __init__(
self,
data: ASyncSenseable,
scale: Scale,
variant_id: str,
variant_name: str,
trends_coordinator: DataUpdateCoordinator[Any],
trends_coordinator: SenseTrendCoordinator,
sense_monitor_id: str,
) -> None:
"""Initialize the Sense sensor."""
super().__init__(trends_coordinator)
self._attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}"
self._attr_unique_id = (
f"{sense_monitor_id}-{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}"
super().__init__(
trends_coordinator,
sense_monitor_id,
f"{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}",
)
self._attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}"
self._data = data
self._scale = scale
self._variant_id = variant_id
@ -279,41 +255,29 @@ class SenseTrendsSensor(CoordinatorEntity, SensorEntity):
return None
class SenseDevicePowerSensor(SensorEntity):
class SenseDevicePowerSensor(SenseBaseSensor):
"""Implementation of a Sense energy device."""
_attr_available = False
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_native_unit_of_measurement = UnitOfPower.WATT
_attr_attribution = ATTRIBUTION
_attr_device_class = SensorDeviceClass.POWER
_attr_should_poll = False
def __init__(self, device: SenseDevice, sense_monitor_id: str) -> None:
def __init__(
self,
device: SenseDevice,
sense_monitor_id: str,
realtime_coordinator: SenseRealtimeCoordinator,
) -> None:
"""Initialize the Sense binary sensor."""
super().__init__(
realtime_coordinator, sense_monitor_id, f"{device.id}-{CONSUMPTION_ID}"
)
self._attr_name = f"{device.name} {CONSUMPTION_NAME}"
self._id = device.id
self._sense_monitor_id = sense_monitor_id
self._attr_unique_id = f"{sense_monitor_id}-{self._id}-{CONSUMPTION_ID}"
self._attr_icon = sense_to_mdi(device.icon)
self._device = device
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
)
)
@callback
def _async_update_from_data(self) -> None:
"""Get the latest data, update state. Must not do I/O."""
new_state = self._device.power_w
if self._attr_available and self._attr_native_value == new_state:
return
self._attr_native_value = new_state
self._attr_available = True
self.async_write_ha_state()
@property
def native_value(self) -> float:
"""Return the state of the sensor."""
return self._device.power_w

View File

@ -45,7 +45,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': 'off',
})
# ---
# name: test_binary_sensors[binary_sensor.oven-entry]
@ -94,6 +94,6 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': 'off',
})
# ---

View File

@ -410,7 +410,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '100.0',
})
# ---
# name: test_sensors[sensor.daily_from_grid-entry]
@ -823,7 +823,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '500',
})
# ---
# name: test_sensors[sensor.energy_usage-entry]
@ -875,7 +875,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '100',
})
# ---
# name: test_sensors[sensor.l1_voltage-entry]
@ -927,7 +927,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '120',
})
# ---
# name: test_sensors[sensor.l2_voltage-entry]
@ -979,7 +979,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '240',
})
# ---
# name: test_sensors[sensor.monthly_from_grid-entry]
@ -1393,7 +1393,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
'state': '50.0',
})
# ---
# name: test_sensors[sensor.weekly_from_grid-entry]

View File

@ -7,7 +7,7 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.sense.const import ACTIVE_UPDATE_RATE
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform
from homeassistant.const import STATE_OFF, STATE_ON, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util.dt import utcnow
@ -40,15 +40,6 @@ async def test_on_off_sensors(
await setup_platform(hass, config_entry, BINARY_SENSOR_DOMAIN)
device_1, device_2 = mock_sense.devices
state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}")
assert state.state == STATE_UNAVAILABLE
state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}")
assert state.state == STATE_UNAVAILABLE
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done()
state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}")
assert state.state == STATE_OFF

View File

@ -9,7 +9,7 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components.sense.const import ACTIVE_UPDATE_RATE, CONSUMPTION_ID
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import STATE_UNAVAILABLE, Platform
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util.dt import utcnow
@ -40,19 +40,11 @@ async def test_device_power_sensors(
config_entry: MockConfigEntry,
) -> None:
"""Test the Sense device power sensors."""
await setup_platform(hass, config_entry, SENSOR_DOMAIN)
device_1, device_2 = mock_sense.devices
state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_{CONSUMPTION_ID}")
assert state.state == STATE_UNAVAILABLE
state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_{CONSUMPTION_ID}")
assert state.state == STATE_UNAVAILABLE
device_1.power_w = 0
device_2.power_w = 0
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done()
await setup_platform(hass, config_entry, SENSOR_DOMAIN)
device_1, device_2 = mock_sense.devices
state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_{CONSUMPTION_ID}")
assert state.state == "0"
@ -90,20 +82,10 @@ async def test_voltage_sensors(
) -> None:
"""Test the Sense voltage sensors."""
type(mock_sense).active_voltage = PropertyMock(return_value=[0, 0])
type(mock_sense).active_voltage = PropertyMock(return_value=[120, 121])
await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.l1_voltage")
assert state.state == STATE_UNAVAILABLE
state = hass.states.get("sensor.l2_voltage")
assert state.state == STATE_UNAVAILABLE
type(mock_sense).active_voltage = PropertyMock(return_value=[120, 121])
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done()
state = hass.states.get("sensor.l1_voltage")
assert state.state == "120"
@ -129,18 +111,10 @@ async def test_active_power_sensors(
) -> None:
"""Test the Sense power sensors."""
await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.energy_usage")
assert state.state == STATE_UNAVAILABLE
state = hass.states.get("sensor.energy_production")
assert state.state == STATE_UNAVAILABLE
type(mock_sense).active_power = PropertyMock(return_value=400)
type(mock_sense).active_solar_power = PropertyMock(return_value=500)
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done()
await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.energy_usage")
assert state.state == "400"