227 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
"""Support for mill wifi-enabled home heaters."""
 | 
						|
from __future__ import annotations
 | 
						|
 | 
						|
import mill
 | 
						|
 | 
						|
from homeassistant.components.sensor import (
 | 
						|
    SensorDeviceClass,
 | 
						|
    SensorEntity,
 | 
						|
    SensorEntityDescription,
 | 
						|
    SensorStateClass,
 | 
						|
)
 | 
						|
from homeassistant.config_entries import ConfigEntry
 | 
						|
from homeassistant.const import (
 | 
						|
    CONCENTRATION_PARTS_PER_BILLION,
 | 
						|
    CONCENTRATION_PARTS_PER_MILLION,
 | 
						|
    CONF_IP_ADDRESS,
 | 
						|
    CONF_USERNAME,
 | 
						|
    PERCENTAGE,
 | 
						|
    EntityCategory,
 | 
						|
    UnitOfEnergy,
 | 
						|
    UnitOfPower,
 | 
						|
    UnitOfTemperature,
 | 
						|
)
 | 
						|
from homeassistant.core import HomeAssistant, callback
 | 
						|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
 | 
						|
from homeassistant.helpers.entity import DeviceInfo
 | 
						|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
 | 
						|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
 | 
						|
 | 
						|
from .const import (
 | 
						|
    BATTERY,
 | 
						|
    CLOUD,
 | 
						|
    CONNECTION_TYPE,
 | 
						|
    CONSUMPTION_TODAY,
 | 
						|
    CONSUMPTION_YEAR,
 | 
						|
    DOMAIN,
 | 
						|
    ECO2,
 | 
						|
    HUMIDITY,
 | 
						|
    LOCAL,
 | 
						|
    MANUFACTURER,
 | 
						|
    TEMPERATURE,
 | 
						|
    TVOC,
 | 
						|
)
 | 
						|
 | 
						|
HEATER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=CONSUMPTION_YEAR,
 | 
						|
        device_class=SensorDeviceClass.ENERGY,
 | 
						|
        native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
 | 
						|
        state_class=SensorStateClass.TOTAL_INCREASING,
 | 
						|
        name="Year consumption",
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=CONSUMPTION_TODAY,
 | 
						|
        device_class=SensorDeviceClass.ENERGY,
 | 
						|
        native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
 | 
						|
        state_class=SensorStateClass.TOTAL_INCREASING,
 | 
						|
        name="Day consumption",
 | 
						|
    ),
 | 
						|
)
 | 
						|
 | 
						|
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=TEMPERATURE,
 | 
						|
        device_class=SensorDeviceClass.TEMPERATURE,
 | 
						|
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
 | 
						|
        name="Temperature",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=HUMIDITY,
 | 
						|
        device_class=SensorDeviceClass.HUMIDITY,
 | 
						|
        native_unit_of_measurement=PERCENTAGE,
 | 
						|
        name="Humidity",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=BATTERY,
 | 
						|
        device_class=SensorDeviceClass.BATTERY,
 | 
						|
        native_unit_of_measurement=PERCENTAGE,
 | 
						|
        name="Battery",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
        entity_category=EntityCategory.DIAGNOSTIC,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=ECO2,
 | 
						|
        device_class=SensorDeviceClass.CO2,
 | 
						|
        native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
 | 
						|
        name="Estimated CO2",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key=TVOC,
 | 
						|
        native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
 | 
						|
        name="TVOC",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
)
 | 
						|
 | 
						|
LOCAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
 | 
						|
    SensorEntityDescription(
 | 
						|
        key="control_signal",
 | 
						|
        native_unit_of_measurement=PERCENTAGE,
 | 
						|
        name="Control signal",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key="current_power",
 | 
						|
        device_class=SensorDeviceClass.POWER,
 | 
						|
        native_unit_of_measurement=UnitOfPower.WATT,
 | 
						|
        name="Current power",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
    ),
 | 
						|
    SensorEntityDescription(
 | 
						|
        key="raw_ambient_temperature",
 | 
						|
        device_class=SensorDeviceClass.TEMPERATURE,
 | 
						|
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
 | 
						|
        name="Uncalibrated temperature",
 | 
						|
        state_class=SensorStateClass.MEASUREMENT,
 | 
						|
        entity_registry_enabled_default=False,
 | 
						|
    ),
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
async def async_setup_entry(
 | 
						|
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
 | 
						|
) -> None:
 | 
						|
    """Set up the Mill sensor."""
 | 
						|
    if entry.data.get(CONNECTION_TYPE) == LOCAL:
 | 
						|
        mill_data_coordinator = hass.data[DOMAIN][LOCAL][entry.data[CONF_IP_ADDRESS]]
 | 
						|
 | 
						|
        async_add_entities(
 | 
						|
            LocalMillSensor(
 | 
						|
                mill_data_coordinator,
 | 
						|
                entity_description,
 | 
						|
            )
 | 
						|
            for entity_description in LOCAL_SENSOR_TYPES
 | 
						|
        )
 | 
						|
        return
 | 
						|
 | 
						|
    mill_data_coordinator = hass.data[DOMAIN][CLOUD][entry.data[CONF_USERNAME]]
 | 
						|
 | 
						|
    entities = [
 | 
						|
        MillSensor(
 | 
						|
            mill_data_coordinator,
 | 
						|
            entity_description,
 | 
						|
            mill_device,
 | 
						|
        )
 | 
						|
        for mill_device in mill_data_coordinator.data.values()
 | 
						|
        for entity_description in (
 | 
						|
            HEATER_SENSOR_TYPES
 | 
						|
            if isinstance(mill_device, mill.Heater)
 | 
						|
            else SENSOR_TYPES
 | 
						|
        )
 | 
						|
    ]
 | 
						|
 | 
						|
    async_add_entities(entities)
 | 
						|
 | 
						|
 | 
						|
class MillSensor(CoordinatorEntity, SensorEntity):
 | 
						|
    """Representation of a Mill Sensor device."""
 | 
						|
 | 
						|
    def __init__(self, coordinator, entity_description, mill_device):
 | 
						|
        """Initialize the sensor."""
 | 
						|
        super().__init__(coordinator)
 | 
						|
 | 
						|
        self._id = mill_device.device_id
 | 
						|
        self.entity_description = entity_description
 | 
						|
        self._available = False
 | 
						|
 | 
						|
        self._attr_name = f"{mill_device.name} {entity_description.name}"
 | 
						|
        self._attr_unique_id = f"{mill_device.device_id}_{entity_description.key}"
 | 
						|
        self._attr_device_info = DeviceInfo(
 | 
						|
            identifiers={(DOMAIN, mill_device.device_id)},
 | 
						|
            name=self.name,
 | 
						|
            manufacturer=MANUFACTURER,
 | 
						|
        )
 | 
						|
        if isinstance(mill_device, mill.Heater):
 | 
						|
            self._attr_device_info["model"] = f"Generation {mill_device.generation}"
 | 
						|
        elif isinstance(mill_device, mill.Sensor):
 | 
						|
            self._attr_device_info["model"] = "Mill Sense Air"
 | 
						|
        self._update_attr(mill_device)
 | 
						|
 | 
						|
    @callback
 | 
						|
    def _handle_coordinator_update(self) -> None:
 | 
						|
        """Handle updated data from the coordinator."""
 | 
						|
        self._update_attr(self.coordinator.data[self._id])
 | 
						|
        self.async_write_ha_state()
 | 
						|
 | 
						|
    @property
 | 
						|
    def available(self) -> bool:
 | 
						|
        """Return True if entity is available."""
 | 
						|
        return super().available and self._available
 | 
						|
 | 
						|
    @callback
 | 
						|
    def _update_attr(self, device):
 | 
						|
        self._available = device.available
 | 
						|
        self._attr_native_value = getattr(device, self.entity_description.key)
 | 
						|
 | 
						|
 | 
						|
class LocalMillSensor(CoordinatorEntity, SensorEntity):
 | 
						|
    """Representation of a Mill Sensor device."""
 | 
						|
 | 
						|
    def __init__(self, coordinator, entity_description):
 | 
						|
        """Initialize the sensor."""
 | 
						|
        super().__init__(coordinator)
 | 
						|
 | 
						|
        self.entity_description = entity_description
 | 
						|
        self._attr_name = (
 | 
						|
            f"{coordinator.mill_data_connection.name} {entity_description.name}"
 | 
						|
        )
 | 
						|
        if mac := coordinator.mill_data_connection.mac_address:
 | 
						|
            self._attr_unique_id = f"{mac}_{entity_description.key}"
 | 
						|
            self._attr_device_info = DeviceInfo(
 | 
						|
                connections={(CONNECTION_NETWORK_MAC, mac)},
 | 
						|
                configuration_url=self.coordinator.mill_data_connection.url,
 | 
						|
                manufacturer=MANUFACTURER,
 | 
						|
                model="Generation 3",
 | 
						|
                name=coordinator.mill_data_connection.name,
 | 
						|
                sw_version=coordinator.mill_data_connection.version,
 | 
						|
            )
 | 
						|
 | 
						|
    @property
 | 
						|
    def native_value(self):
 | 
						|
        """Return the native value of the sensor."""
 | 
						|
        return self.coordinator.data[self.entity_description.key]
 |