"""Support for monitoring a Sense energy sensor.""" from datetime import datetime from sense_energy import ASyncSenseable, Scale from sense_energy.sense_api import SenseDevice from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorStateClass, ) from homeassistant.const import ( PERCENTAGE, UnitOfElectricPotential, UnitOfEnergy, UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import SenseConfigEntry from .const import ( ACTIVE_TYPE, CONSUMPTION_ID, CONSUMPTION_NAME, FROM_GRID_ID, FROM_GRID_NAME, NET_PRODUCTION_ID, NET_PRODUCTION_NAME, PRODUCTION_ID, PRODUCTION_NAME, PRODUCTION_PCT_ID, PRODUCTION_PCT_NAME, SOLAR_POWERED_ID, SOLAR_POWERED_NAME, TO_GRID_ID, TO_GRID_NAME, ) from .coordinator import SenseRealtimeCoordinator, SenseTrendCoordinator from .entity import SenseDeviceEntity, SenseEntity # Sensor types/ranges TRENDS_SENSOR_TYPES = { Scale.DAY: "Daily", Scale.WEEK: "Weekly", Scale.MONTH: "Monthly", Scale.YEAR: "Yearly", Scale.CYCLE: "Bill", } # Production/consumption variants SENSOR_VARIANTS = [(PRODUCTION_ID, PRODUCTION_NAME), (CONSUMPTION_ID, CONSUMPTION_NAME)] # Trend production/consumption variants TREND_SENSOR_VARIANTS = [ *SENSOR_VARIANTS, (PRODUCTION_PCT_ID, PRODUCTION_PCT_NAME), (NET_PRODUCTION_ID, NET_PRODUCTION_NAME), (FROM_GRID_ID, FROM_GRID_NAME), (TO_GRID_ID, TO_GRID_NAME), (SOLAR_POWERED_ID, SOLAR_POWERED_NAME), ] async def async_setup_entry( hass: HomeAssistant, config_entry: SenseConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """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 await trends_coordinator.async_request_refresh() sense_monitor_id = data.sense_monitor_id entities: list[SensorEntity] = [] for device in config_entry.runtime_data.data.devices: entities.append( SenseDevicePowerSensor(device, sense_monitor_id, realtime_coordinator) ) entities.extend( SenseDeviceEnergySensor(device, scale, trends_coordinator, sense_monitor_id) for scale in Scale ) for variant_id, variant_name in SENSOR_VARIANTS: entities.append( SensePowerSensor( data, sense_monitor_id, variant_id, variant_name, realtime_coordinator ) ) entities.extend( SenseVoltageSensor(data, i, sense_monitor_id, realtime_coordinator) for i in range(len(data.active_voltage)) ) for scale in Scale: for variant_id, variant_name in TREND_SENSOR_VARIANTS: entities.append( SenseTrendsSensor( data, scale, variant_id, variant_name, trends_coordinator, sense_monitor_id, ) ) async_add_entities(entities) class SensePowerSensor(SenseEntity, SensorEntity): """Implementation of a Sense energy sensor.""" _attr_device_class = SensorDeviceClass.POWER _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_state_class = SensorStateClass.MEASUREMENT def __init__( self, gateway: ASyncSenseable, sense_monitor_id: str, variant_id: str, variant_name: str, realtime_coordinator: SenseRealtimeCoordinator, ) -> None: """Initialize the Sense sensor.""" super().__init__( gateway, realtime_coordinator, sense_monitor_id, f"{ACTIVE_TYPE}-{variant_id}", ) self._attr_name = variant_name self._variant_id = variant_id @property def native_value(self) -> float: """Return the state of the sensor.""" return round( self._gateway.active_solar_power if self._variant_id == PRODUCTION_ID else self._gateway.active_power ) class SenseVoltageSensor(SenseEntity, SensorEntity): """Implementation of a Sense energy voltage sensor.""" _attr_device_class = SensorDeviceClass.VOLTAGE _attr_state_class = SensorStateClass.MEASUREMENT _attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT def __init__( self, gateway: ASyncSenseable, index: int, sense_monitor_id: str, realtime_coordinator: SenseRealtimeCoordinator, ) -> None: """Initialize the Sense sensor.""" super().__init__( gateway, realtime_coordinator, sense_monitor_id, f"L{index + 1}" ) self._attr_name = f"L{index + 1} Voltage" self._voltage_index = index @property def native_value(self) -> float: """Return the state of the sensor.""" return round(self._gateway.active_voltage[self._voltage_index], 1) class SenseTrendsSensor(SenseEntity, SensorEntity): """Implementation of a Sense energy sensor.""" def __init__( self, gateway: ASyncSenseable, scale: Scale, variant_id: str, variant_name: str, trends_coordinator: SenseTrendCoordinator, sense_monitor_id: str, ) -> None: """Initialize the Sense sensor.""" super().__init__( gateway, trends_coordinator, sense_monitor_id, f"{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}", ) self._attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}" self._scale = scale self._variant_id = variant_id self._had_any_update = False if variant_id in [PRODUCTION_PCT_ID, SOLAR_POWERED_ID]: self._attr_native_unit_of_measurement = PERCENTAGE self._attr_entity_registry_enabled_default = False self._attr_state_class = None self._attr_device_class = None else: self._attr_device_class = SensorDeviceClass.ENERGY self._attr_state_class = SensorStateClass.TOTAL self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR @property def native_value(self) -> float: """Return the state of the sensor.""" return round(self._gateway.get_stat(self._scale, self._variant_id), 1) @property def last_reset(self) -> datetime | None: """Return the time when the sensor was last reset, if any.""" if self._attr_state_class == SensorStateClass.TOTAL: return self._gateway.trend_start(self._scale) return None class SenseDevicePowerSensor(SenseDeviceEntity, SensorEntity): """Implementation of a Sense energy device.""" _attr_state_class = SensorStateClass.MEASUREMENT _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_device_class = SensorDeviceClass.POWER def __init__( self, device: SenseDevice, sense_monitor_id: str, coordinator: SenseRealtimeCoordinator, ) -> None: """Initialize the Sense device sensor.""" super().__init__( device, coordinator, sense_monitor_id, f"{device.id}-{CONSUMPTION_ID}" ) @property def native_value(self) -> float: """Return the state of the sensor.""" return self._device.power_w class SenseDeviceEnergySensor(SenseDeviceEntity, SensorEntity): """Implementation of a Sense device energy sensor.""" _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_state_class = SensorStateClass.TOTAL_INCREASING _attr_device_class = SensorDeviceClass.ENERGY def __init__( self, device: SenseDevice, scale: Scale, coordinator: SenseTrendCoordinator, sense_monitor_id: str, ) -> None: """Initialize the Sense device sensor.""" super().__init__( device, coordinator, sense_monitor_id, f"{device.id}-{TRENDS_SENSOR_TYPES[scale].lower()}-energy", ) self._attr_translation_key = f"{TRENDS_SENSOR_TYPES[scale].lower()}_energy" self._attr_suggested_display_precision = 2 self._scale = scale self._device = device @property def native_value(self) -> float: """Return the state of the sensor.""" return self._device.energy_kwh[self._scale]