Files
gree-controller/home-assistant/custom_components/gree_controller/sensor.py
T
2026-08-24 14:05:43 +02:00

127 lines
4.6 KiB
Python

"""Automation-plan sensors exposed by GREE Controller."""
from __future__ import annotations
from typing import Any
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import GreeControllerRuntimeData
from .const import DOMAIN
from .coordinator import GreeControllerCoordinator
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Create whole-house and per-zone automation-plan sensors."""
runtime: GreeControllerRuntimeData = entry.runtime_data
entities: list[SensorEntity] = [GreeControllerHousePlanSensor(runtime.coordinator)]
for zone in runtime.coordinator.plan.get("zones", []):
if zone.get("zone_id"):
entities.append(GreeControllerZonePlanSensor(runtime.coordinator, str(zone["zone_id"])))
async_add_entities(entities)
class GreeControllerHousePlanSensor(CoordinatorEntity[GreeControllerCoordinator], SensorEntity):
"""Summary of the current whole-house control plan."""
_attr_has_entity_name = True
_attr_name = "Automation plan"
_attr_unique_id = "house-control-plan"
_unrecorded_attributes = frozenset({"next_events", "rules"})
@property
def native_value(self) -> str:
return str(self.coordinator.plan.get("house_mode") or "unknown")
@property
def device_info(self) -> DeviceInfo:
return DeviceInfo(
identifiers={(DOMAIN, "controller")},
name="GREE Controller",
manufacturer="GREE Controller",
model="Local controller",
)
@property
def extra_state_attributes(self) -> dict[str, Any]:
plan = self.coordinator.plan
zones = plan.get("zones", [])
return {
"generated_at": plan.get("generated_at"),
"outdoor_temperature": plan.get("outdoor_temperature"),
"control_strategy": plan.get("control_strategy"),
"enabled_zones": sum(bool(zone.get("enabled")) for zone in zones),
"demanding_zones": sum(bool(zone.get("enabled")) and bool(zone.get("demand")) for zone in zones),
"next_events": plan.get("next_events", []),
"rules": plan.get("rules", []),
}
class GreeControllerZonePlanSensor(CoordinatorEntity[GreeControllerCoordinator], SensorEntity):
"""Readable automation plan for one controller zone."""
_attr_has_entity_name = True
_attr_name = "Control plan"
_unrecorded_attributes = frozenset({"next_events"})
def __init__(self, coordinator: GreeControllerCoordinator, zone_id: str) -> None:
super().__init__(coordinator)
self._zone_id = zone_id
self._attr_unique_id = f"{zone_id}-control-plan"
@property
def _zone(self) -> dict[str, Any]:
for zone in self.coordinator.plan.get("zones", []):
if str(zone.get("zone_id")) == self._zone_id:
return zone
return {}
@property
def available(self) -> bool:
return super().available and bool(self._zone)
@property
def native_value(self) -> str:
zone = self._zone
if not zone.get("enabled", False):
return "disabled"
return "requesting" if zone.get("demand", False) else "satisfied"
@property
def device_info(self) -> DeviceInfo:
zone = self._zone
return DeviceInfo(
identifiers={(DOMAIN, f"zone:{self._zone_id}")},
name=str(zone.get("zone_name") or self._zone_id),
manufacturer="GREE Controller",
model="Zone thermostat",
via_device=(DOMAIN, "controller"),
)
@property
def extra_state_attributes(self) -> dict[str, Any]:
zone = self._zone
return {
"zone_id": self._zone_id,
"device_id": zone.get("device_id"),
"device_name": zone.get("device_name"),
"mode": zone.get("mode"),
"preset": zone.get("preset"),
"current_temperature": zone.get("current_temperature"),
"target_temperature": zone.get("target_temperature"),
"device_setpoint": zone.get("device_setpoint"),
"control_source": zone.get("control_source"),
"manual_override_until": zone.get("manual_override_until"),
"current_schedule": zone.get("current_schedule_name"),
"next_events": zone.get("next_events", []),
}