201 lines
7.8 KiB
Python
201 lines
7.8 KiB
Python
"""House, zone and climate-group 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"])))
|
|
for group_id in runtime.coordinator.groups:
|
|
entities.append(GreeControllerGroupPlanSensor(runtime.coordinator, group_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", [])
|
|
groups = list(self.coordinator.groups.values())
|
|
return {
|
|
"generated_at": plan.get("generated_at"),
|
|
"outdoor_temperature": plan.get("outdoor_temperature"),
|
|
"control_strategy": plan.get("control_strategy"),
|
|
"work_profile": plan.get("house_preset"),
|
|
"master_power": bool(plan.get("house_power", False)),
|
|
"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),
|
|
"groups": len(groups),
|
|
"enabled_groups": sum(bool(group.get("power_enabled")) for group in groups),
|
|
"demanding_groups": sum(int(group.get("demanding_zones") or 0) > 0 for group in groups),
|
|
"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", []),
|
|
}
|
|
|
|
|
|
class GreeControllerGroupPlanSensor(CoordinatorEntity[GreeControllerCoordinator], SensorEntity):
|
|
"""Readable status and automation plan for one climate group."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_name = "Control plan"
|
|
_unrecorded_attributes = frozenset({"next_events", "zone_ids", "zone_names", "members"})
|
|
|
|
def __init__(self, coordinator: GreeControllerCoordinator, group_id: str) -> None:
|
|
super().__init__(coordinator)
|
|
self._group_id = group_id
|
|
self._attr_unique_id = f"{group_id}-group-control-plan"
|
|
|
|
@property
|
|
def _group(self) -> dict[str, Any]:
|
|
return self.coordinator.groups.get(self._group_id, {})
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
return super().available and bool(self._group)
|
|
|
|
@property
|
|
def native_value(self) -> str:
|
|
group = self._group
|
|
if not group.get("power_enabled", False):
|
|
return "off"
|
|
if not group.get("effective_power", False):
|
|
return "master_off"
|
|
if group.get("mode") == "house" and group.get("house_mode") == "off":
|
|
return "paused"
|
|
return "requesting" if int(group.get("demanding_zones") or 0) > 0 else "satisfied"
|
|
|
|
@property
|
|
def device_info(self) -> DeviceInfo:
|
|
group = self._group
|
|
return DeviceInfo(
|
|
identifiers={(DOMAIN, f"group:{self._group_id}")},
|
|
name=str(group.get("name") or self._group_id),
|
|
manufacturer="GREE Controller",
|
|
model="Climate group",
|
|
via_device=(DOMAIN, "controller"),
|
|
)
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
group = self._group
|
|
return {
|
|
"group_id": self._group_id,
|
|
"power_enabled": bool(group.get("power_enabled", False)),
|
|
"effective_power": bool(group.get("effective_power", False)),
|
|
"mode": group.get("mode"),
|
|
"global_house_mode": group.get("house_mode"),
|
|
"work_profile": group.get("preset"),
|
|
"zone_count": group.get("zone_count"),
|
|
"enabled_zones": group.get("enabled_zones"),
|
|
"active_zones": group.get("active_zones"),
|
|
"demanding_zones": group.get("demanding_zones"),
|
|
"device_count": group.get("device_count"),
|
|
"online_devices": group.get("online_devices"),
|
|
"current_temperature": group.get("current_temperature"),
|
|
"zone_ids": group.get("zone_ids", []),
|
|
"zone_names": group.get("zone_names", []),
|
|
"members": group.get("members", []),
|
|
"next_events": group.get("next_events", []),
|
|
}
|