|
| 1 | +from datetime import date, datetime |
| 2 | +from dataclasses import dataclass |
| 3 | +from enum import Enum |
| 4 | +from typing import Callable |
| 5 | + |
| 6 | +from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, SensorStateClass |
| 7 | +from homeassistant.const import UnitOfPower, UnitOfEnergy |
| 8 | +from homeassistant.helpers.device_registry import DeviceInfo |
| 9 | +from homeassistant.helpers.typing import StateType |
| 10 | + |
| 11 | +from powersensor_local import VirtualHousehold |
| 12 | + |
| 13 | +from .const import DOMAIN |
| 14 | + |
| 15 | +class HouseholdMeasurements(Enum): |
| 16 | + POWER_HOME_USE = 1 |
| 17 | + POWER_FROM_GRID = 2 |
| 18 | + POWER_TO_GRID = 3 |
| 19 | + POWER_SOLAR_GENERATION = 4 |
| 20 | + ENERGY_HOME_USE = 5 |
| 21 | + ENERGY_FROM_GRID = 6 |
| 22 | + ENERGY_TO_GRID = 7 |
| 23 | + ENERGY_SOLAR_GENERATION = 8 |
| 24 | + |
| 25 | +class UnitType(Enum): |
| 26 | + WATT = UnitOfPower.WATT |
| 27 | + KWH = UnitOfEnergy.KILO_WATT_HOUR |
| 28 | + |
| 29 | +@dataclass |
| 30 | +class EntityConfig: |
| 31 | + name : str |
| 32 | + device_class : SensorDeviceClass |
| 33 | + state_class : SensorStateClass | None |
| 34 | + unit : UnitType |
| 35 | + formatter: Callable |
| 36 | + precision: int |
| 37 | + event: str |
| 38 | + |
| 39 | +FMT_INT = lambda f: int(f) |
| 40 | +FMT_WS_TO_KWH = lambda f: float(f)/3600000 |
| 41 | + |
| 42 | +class PowersensorHouseholdEntity(SensorEntity): |
| 43 | + """Powersensor Virtual Household entity""" |
| 44 | + |
| 45 | + should_poll = False |
| 46 | + _attr_has_entity_name = True |
| 47 | + _attr_available = True |
| 48 | + |
| 49 | + _ENTITY_CONFIGS = { |
| 50 | + HouseholdMeasurements.POWER_HOME_USE : EntityConfig( |
| 51 | + "Power - Home use", SensorDeviceClass.POWER, None, UnitType.WATT, FMT_INT, 0, "home_usage"), |
| 52 | + HouseholdMeasurements.POWER_FROM_GRID : EntityConfig( |
| 53 | + "Power - From grid", SensorDeviceClass.POWER, None, UnitType.WATT, FMT_INT, 0, "from_grid"), |
| 54 | + HouseholdMeasurements.POWER_TO_GRID : EntityConfig( |
| 55 | + "Power - To grid", SensorDeviceClass.POWER, None, UnitType.WATT, FMT_INT, 0, "to_grid"), |
| 56 | + HouseholdMeasurements.POWER_SOLAR_GENERATION : EntityConfig( |
| 57 | + "Power - Solar generation", SensorDeviceClass.POWER, None, UnitType.WATT, FMT_INT, 0, "solar_generation"), |
| 58 | + |
| 59 | + HouseholdMeasurements.ENERGY_HOME_USE : EntityConfig( |
| 60 | + "Energy - Home usage", SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, UnitType.KWH, FMT_WS_TO_KWH, 3, "home_usage_summation"), |
| 61 | + HouseholdMeasurements.ENERGY_FROM_GRID : EntityConfig( |
| 62 | + "Energy - From grid", SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, UnitType.KWH, FMT_WS_TO_KWH, 3, "from_grid_summation"), |
| 63 | + HouseholdMeasurements.ENERGY_TO_GRID : EntityConfig( |
| 64 | + "Energy - To grid", SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, UnitType.KWH, FMT_WS_TO_KWH, 3, "to_grid_summation"), |
| 65 | + HouseholdMeasurements.ENERGY_SOLAR_GENERATION : EntityConfig( |
| 66 | + "Energy - Solar generation", SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, UnitType.KWH, FMT_WS_TO_KWH, 3, "solar_generation_summation"), |
| 67 | + } |
| 68 | + |
| 69 | + def __init__(self, vhh: VirtualHousehold, measurement_type: HouseholdMeasurements): |
| 70 | + """Initialize the entity.""" |
| 71 | + self._vhh = vhh |
| 72 | + self._config = self._ENTITY_CONFIGS[measurement_type] |
| 73 | + |
| 74 | + self._attr_name = self._config.name |
| 75 | + self._attr_unique_id = f"{DOMAIN}_vhh_{self._config.event}" |
| 76 | + self._attr_device_class = self._config.device_class |
| 77 | + self._attr_state_class = self._config.state_class |
| 78 | + self._attr_native_unit_of_measurement = self._config.unit |
| 79 | + self._attr_suggested_display_precision = self._config.precision |
| 80 | + |
| 81 | + @property |
| 82 | + def device_info(self) -> DeviceInfo: |
| 83 | + return { |
| 84 | + 'identifiers': {(DOMAIN, "vhh")}, |
| 85 | + 'manufacturer': "Powersensor", |
| 86 | + 'model': "Virtual", |
| 87 | + 'name': "Powersensor Household View", |
| 88 | + } |
| 89 | + |
| 90 | + async def async_added_to_hass(self): |
| 91 | + self._vhh.subscribe(self._config.event, self._on_event) |
| 92 | + |
| 93 | + async def async_will_remove_from_hass(self): |
| 94 | + self._vhh.unsubscribe(self._config.event, self._on_event) |
| 95 | + |
| 96 | + async def _on_event(self, _, msg): |
| 97 | + val = None |
| 98 | + if self._config.unit == UnitType.WATT: |
| 99 | + key = "watts" |
| 100 | + if key in msg: |
| 101 | + val = msg[key] |
| 102 | + elif self._config.unit == UnitType.KWH: |
| 103 | + key = "summation_joules" |
| 104 | + if key in msg: |
| 105 | + val = msg[key] |
| 106 | + key = "summation_resettime_utc" |
| 107 | + if key in msg: |
| 108 | + self._attr_last_reset = datetime.fromtimestamp(msg[key]) |
| 109 | + if val is not None: |
| 110 | + self._attr_native_value = self._config.formatter(val) |
| 111 | + self.async_write_ha_state() |
0 commit comments