Add Sense Devices for entities (#129182)

This commit is contained in:
Keilin Bickar 2024-10-29 12:44:19 -04:00 committed by GitHub
parent ca3d13b5cc
commit b43bc3f32d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 414 additions and 397 deletions

View File

@ -113,7 +113,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: SenseConfigEntry) -> boo
) )
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True

View File

@ -11,11 +11,11 @@ from homeassistant.components.binary_sensor import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import SenseConfigEntry from . import SenseConfigEntry
from .const import ATTRIBUTION, DOMAIN, MDI_ICONS from .const import DOMAIN
from .coordinator import SenseRealtimeCoordinator from .coordinator import SenseRealtimeCoordinator
from .entity import SenseDeviceEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -30,7 +30,7 @@ async def async_setup_entry(
realtime_coordinator = config_entry.runtime_data.rt realtime_coordinator = config_entry.runtime_data.rt
devices = [ devices = [
SenseBinarySensor(device, sense_monitor_id, realtime_coordinator) SenseBinarySensor(device, realtime_coordinator, sense_monitor_id)
for device in config_entry.runtime_data.data.devices for device in config_entry.runtime_data.data.devices
] ]
@ -39,33 +39,20 @@ async def async_setup_entry(
async_add_entities(devices) async_add_entities(devices)
def sense_to_mdi(sense_icon: str) -> str: class SenseBinarySensor(SenseDeviceEntity, BinarySensorEntity):
"""Convert sense icon to mdi icon."""
return f"mdi:{MDI_ICONS.get(sense_icon, "power-plug")}"
class SenseBinarySensor(
CoordinatorEntity[SenseRealtimeCoordinator], BinarySensorEntity
):
"""Implementation of a Sense energy device binary sensor.""" """Implementation of a Sense energy device binary sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_device_class = BinarySensorDeviceClass.POWER _attr_device_class = BinarySensorDeviceClass.POWER
def __init__( def __init__(
self, self,
device: SenseDevice, device: SenseDevice,
sense_monitor_id: str,
coordinator: SenseRealtimeCoordinator, coordinator: SenseRealtimeCoordinator,
sense_monitor_id: str,
) -> None: ) -> None:
"""Initialize the Sense binary sensor.""" """Initialize the Sense binary sensor."""
super().__init__(coordinator) super().__init__(device, coordinator, sense_monitor_id, device.id)
self._attr_name = device.name
self._id = device.id self._id = device.id
self._attr_unique_id = f"{sense_monitor_id}-{self._id}"
self._attr_icon = sense_to_mdi(device.icon)
self._device = device
@property @property
def old_unique_id(self) -> str: def old_unique_id(self) -> str:

View File

@ -20,7 +20,7 @@ ACTIVE_TYPE = "active"
ATTRIBUTION = "Data provided by Sense.com" ATTRIBUTION = "Data provided by Sense.com"
CONSUMPTION_NAME = "Usage" CONSUMPTION_NAME = "Energy"
CONSUMPTION_ID = "usage" CONSUMPTION_ID = "usage"
PRODUCTION_NAME = "Production" PRODUCTION_NAME = "Production"
PRODUCTION_ID = "production" PRODUCTION_ID = "production"

View File

@ -0,0 +1,71 @@
"""Base entities for Sense energy."""
from sense_energy import ASyncSenseable
from sense_energy.sense_api import SenseDevice
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ATTRIBUTION, DOMAIN, MDI_ICONS
from .coordinator import SenseCoordinator
def sense_to_mdi(sense_icon: str) -> str:
"""Convert sense icon to mdi icon."""
return f"mdi:{MDI_ICONS.get(sense_icon, "power-plug")}"
class SenseEntity(CoordinatorEntity[SenseCoordinator]):
"""Base implementation of a Sense sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(
self,
gateway: ASyncSenseable,
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}"
self._gateway = gateway
self._attr_device_info = DeviceInfo(
name=f"Sense {sense_monitor_id}",
identifiers={(DOMAIN, sense_monitor_id)},
model="Sense",
manufacturer="Sense Labs, Inc.",
configuration_url="https://home.sense.com",
)
class SenseDeviceEntity(CoordinatorEntity[SenseCoordinator]):
"""Base implementation of a Sense sensor."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(
self,
device: SenseDevice,
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}"
self._device = device
self._attr_icon = sense_to_mdi(device.icon)
self._attr_device_info = DeviceInfo(
name=device.name,
identifiers={(DOMAIN, f"{sense_monitor_id}:{device.id}")},
model="Sense",
manufacturer="Sense Labs, Inc.",
configuration_url="https://home.sense.com",
via_device=(DOMAIN, sense_monitor_id),
)

View File

@ -17,21 +17,15 @@ from homeassistant.const import (
UnitOfPower, UnitOfPower,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import SenseConfigEntry from . import SenseConfigEntry
from .const import ( from .const import (
ACTIVE_NAME,
ACTIVE_TYPE, ACTIVE_TYPE,
ATTRIBUTION,
CONSUMPTION_ID, CONSUMPTION_ID,
CONSUMPTION_NAME, CONSUMPTION_NAME,
DOMAIN,
FROM_GRID_ID, FROM_GRID_ID,
FROM_GRID_NAME, FROM_GRID_NAME,
MDI_ICONS,
NET_PRODUCTION_ID, NET_PRODUCTION_ID,
NET_PRODUCTION_NAME, NET_PRODUCTION_NAME,
PRODUCTION_ID, PRODUCTION_ID,
@ -43,11 +37,8 @@ from .const import (
TO_GRID_ID, TO_GRID_ID,
TO_GRID_NAME, TO_GRID_NAME,
) )
from .coordinator import ( from .coordinator import SenseRealtimeCoordinator, SenseTrendCoordinator
SenseCoordinator, from .entity import SenseDeviceEntity, SenseEntity
SenseRealtimeCoordinator,
SenseTrendCoordinator,
)
# Sensor types/ranges # Sensor types/ranges
TRENDS_SENSOR_TYPES = { TRENDS_SENSOR_TYPES = {
@ -72,11 +63,6 @@ TREND_SENSOR_VARIANTS = [
] ]
def sense_to_mdi(sense_icon: str) -> str:
"""Convert sense icon to mdi icon."""
return f"mdi:{MDI_ICONS.get(sense_icon, 'power-plug')}"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: SenseConfigEntry, config_entry: SenseConfigEntry,
@ -126,24 +112,7 @@ async def async_setup_entry(
async_add_entities(entities) async_add_entities(entities)
class SenseBaseSensor(CoordinatorEntity[SenseCoordinator], SensorEntity): class SensePowerSensor(SenseEntity, 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.""" """Implementation of a Sense energy sensor."""
_attr_device_class = SensorDeviceClass.POWER _attr_device_class = SensorDeviceClass.POWER
@ -152,7 +121,7 @@ class SensePowerSensor(SenseBaseSensor):
def __init__( def __init__(
self, self,
data: ASyncSenseable, gateway: ASyncSenseable,
sense_monitor_id: str, sense_monitor_id: str,
variant_id: str, variant_id: str,
variant_name: str, variant_name: str,
@ -160,23 +129,25 @@ class SensePowerSensor(SenseBaseSensor):
) -> None: ) -> None:
"""Initialize the Sense sensor.""" """Initialize the Sense sensor."""
super().__init__( super().__init__(
realtime_coordinator, sense_monitor_id, f"{ACTIVE_TYPE}-{variant_id}" gateway,
realtime_coordinator,
sense_monitor_id,
f"{ACTIVE_TYPE}-{variant_id}",
) )
self._attr_name = f"{ACTIVE_NAME} {variant_name}" self._attr_name = variant_name
self._data = data
self._variant_id = variant_id self._variant_id = variant_id
@property @property
def native_value(self) -> float: def native_value(self) -> float:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round( return round(
self._data.active_solar_power self._gateway.active_solar_power
if self._variant_id == PRODUCTION_ID if self._variant_id == PRODUCTION_ID
else self._data.active_power else self._gateway.active_power
) )
class SenseVoltageSensor(SenseBaseSensor): class SenseVoltageSensor(SenseEntity, SensorEntity):
"""Implementation of a Sense energy voltage sensor.""" """Implementation of a Sense energy voltage sensor."""
_attr_device_class = SensorDeviceClass.VOLTAGE _attr_device_class = SensorDeviceClass.VOLTAGE
@ -185,29 +156,30 @@ class SenseVoltageSensor(SenseBaseSensor):
def __init__( def __init__(
self, self,
data: ASyncSenseable, gateway: ASyncSenseable,
index: int, index: int,
sense_monitor_id: str, sense_monitor_id: str,
realtime_coordinator: SenseRealtimeCoordinator, realtime_coordinator: SenseRealtimeCoordinator,
) -> None: ) -> None:
"""Initialize the Sense sensor.""" """Initialize the Sense sensor."""
super().__init__(realtime_coordinator, sense_monitor_id, f"L{index + 1}") super().__init__(
gateway, realtime_coordinator, sense_monitor_id, f"L{index + 1}"
)
self._attr_name = f"L{index + 1} Voltage" self._attr_name = f"L{index + 1} Voltage"
self._data = data
self._voltage_index = index self._voltage_index = index
@property @property
def native_value(self) -> float: def native_value(self) -> float:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round(self._data.active_voltage[self._voltage_index], 1) return round(self._gateway.active_voltage[self._voltage_index], 1)
class SenseTrendsSensor(SenseBaseSensor): class SenseTrendsSensor(SenseEntity, SensorEntity):
"""Implementation of a Sense energy sensor.""" """Implementation of a Sense energy sensor."""
def __init__( def __init__(
self, self,
data: ASyncSenseable, gateway: ASyncSenseable,
scale: Scale, scale: Scale,
variant_id: str, variant_id: str,
variant_name: str, variant_name: str,
@ -216,12 +188,12 @@ class SenseTrendsSensor(SenseBaseSensor):
) -> None: ) -> None:
"""Initialize the Sense sensor.""" """Initialize the Sense sensor."""
super().__init__( super().__init__(
gateway,
trends_coordinator, trends_coordinator,
sense_monitor_id, sense_monitor_id,
f"{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}", f"{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}",
) )
self._attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}" self._attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}"
self._data = data
self._scale = scale self._scale = scale
self._variant_id = variant_id self._variant_id = variant_id
self._had_any_update = False self._had_any_update = False
@ -234,28 +206,21 @@ class SenseTrendsSensor(SenseBaseSensor):
self._attr_device_class = SensorDeviceClass.ENERGY self._attr_device_class = SensorDeviceClass.ENERGY
self._attr_state_class = SensorStateClass.TOTAL self._attr_state_class = SensorStateClass.TOTAL
self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
self._attr_device_info = DeviceInfo(
name=f"Sense {sense_monitor_id}",
identifiers={(DOMAIN, sense_monitor_id)},
model="Sense",
manufacturer="Sense Labs, Inc.",
configuration_url="https://home.sense.com",
)
@property @property
def native_value(self) -> float: def native_value(self) -> float:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return round(self._data.get_stat(self._scale, self._variant_id), 1) return round(self._gateway.get_stat(self._scale, self._variant_id), 1)
@property @property
def last_reset(self) -> datetime | None: def last_reset(self) -> datetime | None:
"""Return the time when the sensor was last reset, if any.""" """Return the time when the sensor was last reset, if any."""
if self._attr_state_class == SensorStateClass.TOTAL: if self._attr_state_class == SensorStateClass.TOTAL:
return self._data.trend_start(self._scale) return self._gateway.trend_start(self._scale)
return None return None
class SenseDevicePowerSensor(SenseBaseSensor): class SenseDevicePowerSensor(SenseDeviceEntity, SensorEntity):
"""Implementation of a Sense energy device.""" """Implementation of a Sense energy device."""
_attr_state_class = SensorStateClass.MEASUREMENT _attr_state_class = SensorStateClass.MEASUREMENT
@ -266,16 +231,12 @@ class SenseDevicePowerSensor(SenseBaseSensor):
self, self,
device: SenseDevice, device: SenseDevice,
sense_monitor_id: str, sense_monitor_id: str,
realtime_coordinator: SenseRealtimeCoordinator, coordinator: SenseRealtimeCoordinator,
) -> None: ) -> None:
"""Initialize the Sense binary sensor.""" """Initialize the Sense device sensor."""
super().__init__( super().__init__(
realtime_coordinator, sense_monitor_id, f"{device.id}-{CONSUMPTION_ID}" device, coordinator, sense_monitor_id, f"{device.id}-{CONSUMPTION_ID}"
) )
self._attr_name = f"{device.name} {CONSUMPTION_NAME}"
self._id = device.id
self._attr_icon = sense_to_mdi(device.icon)
self._device = device
@property @property
def native_value(self) -> float: def native_value(self) -> float:

View File

@ -7,14 +7,17 @@ import datetime
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
import pytest import pytest
from sense_energy import Scale
from homeassistant.components.sense.binary_sensor import SenseDevice from homeassistant.components.sense.binary_sensor import SenseDevice
from homeassistant.components.sense.const import DOMAIN from homeassistant.components.sense.const import DOMAIN
from .const import ( from .const import (
DEVICE_1_DAY_ENERGY,
DEVICE_1_ID, DEVICE_1_ID,
DEVICE_1_NAME, DEVICE_1_NAME,
DEVICE_1_POWER, DEVICE_1_POWER,
DEVICE_2_DAY_ENERGY,
DEVICE_2_ID, DEVICE_2_ID,
DEVICE_2_NAME, DEVICE_2_NAME,
DEVICE_2_POWER, DEVICE_2_POWER,
@ -68,12 +71,14 @@ def mock_sense() -> Generator[MagicMock]:
device_1.icon = "car" device_1.icon = "car"
device_1.is_on = False device_1.is_on = False
device_1.power_w = DEVICE_1_POWER device_1.power_w = DEVICE_1_POWER
device_1.energy_kwh[Scale.DAY] = DEVICE_1_DAY_ENERGY
device_2 = SenseDevice(DEVICE_2_ID) device_2 = SenseDevice(DEVICE_2_ID)
device_2.name = DEVICE_2_NAME device_2.name = DEVICE_2_NAME
device_2.icon = "stove" device_2.icon = "stove"
device_2.is_on = False device_2.is_on = False
device_2.power_w = DEVICE_2_POWER device_2.power_w = DEVICE_2_POWER
device_2.energy_kwh[Scale.DAY] = DEVICE_2_DAY_ENERGY
type(gateway).devices = PropertyMock(return_value=[device_1, device_2]) type(gateway).devices = PropertyMock(return_value=[device_1, device_2])
yield gateway yield gateway

View File

@ -1,24 +1,29 @@
"""Cosntants for the Sense integration tests.""" """Cosntants for the Sense integration tests."""
MONITOR_ID = "456"
MOCK_CONFIG = { MOCK_CONFIG = {
"timeout": 6, "timeout": 6,
"email": "test-email", "email": "test-email",
"password": "test-password", "password": "test-password",
"access_token": "ABC", "access_token": "ABC",
"user_id": "123", "user_id": "123",
"monitor_id": "456", "monitor_id": MONITOR_ID,
"device_id": "789", "device_id": "789",
"refresh_token": "XYZ", "refresh_token": "XYZ",
} }
DEVICE_1_NAME = "Car" DEVICE_1_NAME = "Car"
DEVICE_1_ID = "abc123" DEVICE_1_ID = "abc123"
DEVICE_1_ICON = "car-electric" DEVICE_1_ICON = "car-electric"
DEVICE_1_POWER = 100.0 DEVICE_1_POWER = 100.0
DEVICE_1_DAY_ENERGY = 500
DEVICE_2_NAME = "Oven" DEVICE_2_NAME = "Oven"
DEVICE_2_ID = "def456" DEVICE_2_ID = "def456"
DEVICE_2_ICON = "stove" DEVICE_2_ICON = "stove"
DEVICE_2_POWER = 50.0 DEVICE_2_POWER = 50.0
DEVICE_2_DAY_ENERGY = 42
MONITOR_ID = "12345" MONITOR_ID = "12345"

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_binary_sensors[binary_sensor.car-entry] # name: test_binary_sensors[binary_sensor.car_power-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -11,8 +11,8 @@
'disabled_by': None, 'disabled_by': None,
'domain': 'binary_sensor', 'domain': 'binary_sensor',
'entity_category': None, 'entity_category': None,
'entity_id': 'binary_sensor.car', 'entity_id': 'binary_sensor.car_power',
'has_entity_name': False, 'has_entity_name': True,
'hidden_by': None, 'hidden_by': None,
'icon': None, 'icon': None,
'id': <ANY>, 'id': <ANY>,
@ -23,7 +23,7 @@
}), }),
'original_device_class': <BinarySensorDeviceClass.POWER: 'power'>, 'original_device_class': <BinarySensorDeviceClass.POWER: 'power'>,
'original_icon': 'mdi:car-electric', 'original_icon': 'mdi:car-electric',
'original_name': 'Car', 'original_name': 'Power',
'platform': 'sense', 'platform': 'sense',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': 0, 'supported_features': 0,
@ -32,23 +32,23 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_binary_sensors[binary_sensor.car-state] # name: test_binary_sensors[binary_sensor.car_power-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'attribution': 'Data provided by Sense.com', 'attribution': 'Data provided by Sense.com',
'device_class': 'power', 'device_class': 'power',
'friendly_name': 'Car', 'friendly_name': 'Car Power',
'icon': 'mdi:car-electric', 'icon': 'mdi:car-electric',
}), }),
'context': <ANY>, 'context': <ANY>,
'entity_id': 'binary_sensor.car', 'entity_id': 'binary_sensor.car_power',
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'off', 'state': 'off',
}) })
# --- # ---
# name: test_binary_sensors[binary_sensor.oven-entry] # name: test_binary_sensors[binary_sensor.oven_power-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -60,8 +60,8 @@
'disabled_by': None, 'disabled_by': None,
'domain': 'binary_sensor', 'domain': 'binary_sensor',
'entity_category': None, 'entity_category': None,
'entity_id': 'binary_sensor.oven', 'entity_id': 'binary_sensor.oven_power',
'has_entity_name': False, 'has_entity_name': True,
'hidden_by': None, 'hidden_by': None,
'icon': None, 'icon': None,
'id': <ANY>, 'id': <ANY>,
@ -72,7 +72,7 @@
}), }),
'original_device_class': <BinarySensorDeviceClass.POWER: 'power'>, 'original_device_class': <BinarySensorDeviceClass.POWER: 'power'>,
'original_icon': 'mdi:stove', 'original_icon': 'mdi:stove',
'original_name': 'Oven', 'original_name': 'Power',
'platform': 'sense', 'platform': 'sense',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': 0, 'supported_features': 0,
@ -81,16 +81,16 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_binary_sensors[binary_sensor.oven-state] # name: test_binary_sensors[binary_sensor.oven_power-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'attribution': 'Data provided by Sense.com', 'attribution': 'Data provided by Sense.com',
'device_class': 'power', 'device_class': 'power',
'friendly_name': 'Oven', 'friendly_name': 'Oven Power',
'icon': 'mdi:stove', 'icon': 'mdi:stove',
}), }),
'context': <ANY>, 'context': <ANY>,
'entity_id': 'binary_sensor.oven', 'entity_id': 'binary_sensor.oven_power',
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,

File diff suppressed because it is too large Load Diff

View File

@ -40,20 +40,20 @@ async def test_on_off_sensors(
await setup_platform(hass, config_entry, BINARY_SENSOR_DOMAIN) await setup_platform(hass, config_entry, BINARY_SENSOR_DOMAIN)
device_1, device_2 = mock_sense.devices device_1, device_2 = mock_sense.devices
state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}_power")
assert state.state == STATE_OFF assert state.state == STATE_OFF
state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}_power")
assert state.state == STATE_OFF assert state.state == STATE_OFF
device_1.is_on = True device_1.is_on = True
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}_power")
assert state.state == STATE_ON assert state.state == STATE_ON
state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}_power")
assert state.state == STATE_OFF assert state.state == STATE_OFF
device_1.is_on = False device_1.is_on = False
@ -61,8 +61,8 @@ async def test_on_off_sensors(
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_1_NAME.lower()}_power")
assert state.state == STATE_OFF assert state.state == STATE_OFF
state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}") state = hass.states.get(f"binary_sensor.{DEVICE_2_NAME.lower()}_power")
assert state.state == STATE_ON assert state.state == STATE_ON

View File

@ -7,7 +7,7 @@ import pytest
from sense_energy import Scale from sense_energy import Scale
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.sense.const import ACTIVE_UPDATE_RATE, CONSUMPTION_ID from homeassistant.components.sense.const import ACTIVE_UPDATE_RATE
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -15,7 +15,7 @@ from homeassistant.helpers import entity_registry as er
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from . import setup_platform from . import setup_platform
from .const import DEVICE_1_NAME, DEVICE_1_POWER, DEVICE_2_NAME, DEVICE_2_POWER from .const import DEVICE_1_NAME, DEVICE_2_NAME, DEVICE_2_POWER, MONITOR_ID
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
@ -46,31 +46,20 @@ async def test_device_power_sensors(
await setup_platform(hass, config_entry, SENSOR_DOMAIN) await setup_platform(hass, config_entry, SENSOR_DOMAIN)
device_1, device_2 = mock_sense.devices device_1, device_2 = mock_sense.devices
state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_{CONSUMPTION_ID}") state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_power")
assert state.state == "0" assert state.state == "0"
state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_{CONSUMPTION_ID}") state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_power")
assert state.state == "0" assert state.state == "0"
device_1.power_w = DEVICE_1_POWER
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done()
state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_{CONSUMPTION_ID}")
assert state.state == f"{DEVICE_1_POWER:.1f}"
state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_{CONSUMPTION_ID}")
assert state.state == "0"
device_1.power_w = 0
device_2.power_w = DEVICE_2_POWER device_2.power_w = DEVICE_2_POWER
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_{CONSUMPTION_ID}") state = hass.states.get(f"sensor.{DEVICE_1_NAME.lower()}_power")
assert state.state == "0" assert state.state == "0"
state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_{CONSUMPTION_ID}") state = hass.states.get(f"sensor.{DEVICE_2_NAME.lower()}_power")
assert state.state == f"{DEVICE_2_POWER:.1f}" assert state.state == f"{DEVICE_2_POWER:.1f}"
@ -86,20 +75,20 @@ async def test_voltage_sensors(
await setup_platform(hass, config_entry, SENSOR_DOMAIN) await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.l1_voltage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_l1_voltage")
assert state.state == "120" assert state.state == "120"
state = hass.states.get("sensor.l2_voltage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_l2_voltage")
assert state.state == "121" assert state.state == "121"
type(mock_sense).active_voltage = PropertyMock(return_value=[122, 123]) type(mock_sense).active_voltage = PropertyMock(return_value=[122, 123])
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("sensor.l1_voltage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_l1_voltage")
assert state.state == "122" assert state.state == "122"
state = hass.states.get("sensor.l2_voltage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_l2_voltage")
assert state.state == "123" assert state.state == "123"
@ -116,10 +105,10 @@ async def test_active_power_sensors(
await setup_platform(hass, config_entry, SENSOR_DOMAIN) await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.energy_usage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_energy")
assert state.state == "400" assert state.state == "400"
state = hass.states.get("sensor.energy_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_production")
assert state.state == "500" assert state.state == "500"
type(mock_sense).active_power = PropertyMock(return_value=600) type(mock_sense).active_power = PropertyMock(return_value=600)
@ -127,10 +116,10 @@ async def test_active_power_sensors(
async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=ACTIVE_UPDATE_RATE))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("sensor.energy_usage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_energy")
assert state.state == "600" assert state.state == "600"
state = hass.states.get("sensor.energy_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_production")
assert state.state == "700" assert state.state == "700"
@ -153,19 +142,19 @@ async def test_trend_energy_sensors(
await setup_platform(hass, config_entry, SENSOR_DOMAIN) await setup_platform(hass, config_entry, SENSOR_DOMAIN)
state = hass.states.get("sensor.daily_usage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_energy")
assert state.state == "100" assert state.state == "100"
state = hass.states.get("sensor.daily_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_production")
assert state.state == "200" assert state.state == "200"
state = hass.states.get("sensor.daily_from_grid") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_from_grid")
assert state.state == "300" assert state.state == "300"
state = hass.states.get("sensor.daily_to_grid") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_to_grid")
assert state.state == "400" assert state.state == "400"
state = hass.states.get("sensor.daily_net_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_net_production")
assert state.state == "500" assert state.state == "500"
mock_sense.get_stat.side_effect = lambda sensor_type, variant: { mock_sense.get_stat.side_effect = lambda sensor_type, variant: {
@ -180,17 +169,17 @@ async def test_trend_energy_sensors(
async_fire_time_changed(hass, utcnow() + timedelta(seconds=600)) async_fire_time_changed(hass, utcnow() + timedelta(seconds=600))
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("sensor.daily_usage") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_energy")
assert state.state == "1000" assert state.state == "1000"
state = hass.states.get("sensor.daily_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_production")
assert state.state == "2000" assert state.state == "2000"
state = hass.states.get("sensor.daily_from_grid") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_from_grid")
assert state.state == "3000" assert state.state == "3000"
state = hass.states.get("sensor.daily_to_grid") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_to_grid")
assert state.state == "4000" assert state.state == "4000"
state = hass.states.get("sensor.daily_net_production") state = hass.states.get(f"sensor.sense_{MONITOR_ID}_daily_net_production")
assert state.state == "5000" assert state.state == "5000"