mirror of https://github.com/home-assistant/core
283 lines
8.5 KiB
Python
283 lines
8.5 KiB
Python
"""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]
|