This commit is contained in:
Mateusz Gruszczyński
2026-08-24 16:12:31 +02:00
parent 83f744e2cb
commit 66a5d6e5e9
22 changed files with 686 additions and 103 deletions
@@ -47,7 +47,7 @@ async def async_setup_entry(
"""Create climate entities for all devices exposed by the controller."""
runtime: GreeControllerRuntimeData = entry.runtime_data
registry = er.async_get(hass)
entities: list[GreeControllerClimate] = []
entities: list[ClimateEntity] = []
for device_id, device in runtime.coordinator.data.items():
desired_entity_id = runtime.entity_map.get(device_id)
@@ -70,6 +70,11 @@ async def async_setup_entry(
GreeControllerClimate(runtime.coordinator, device_id, desired_entity_id)
)
for zone in runtime.coordinator.plan.get("zones", []):
zone_id = str(zone.get("zone_id") or "").strip()
if zone_id:
entities.append(GreeControllerZoneClimate(runtime.coordinator, zone_id))
async_add_entities(entities)
@@ -166,6 +171,10 @@ class GreeControllerClimate(CoordinatorEntity[GreeControllerCoordinator], Climat
"quiet": bool(device.get("quiet", False)),
"turbo": bool(device.get("turbo", False)),
"light": bool(device.get("light", False)),
"xfan": bool(device.get("xfan", False)),
"air": bool(device.get("air", False)),
"health": bool(device.get("health", False)),
"sleep": bool(device.get("sleep", False)),
"last_seen": device.get("last_seen"),
}
@@ -202,3 +211,109 @@ class GreeControllerClimate(CoordinatorEntity[GreeControllerCoordinator], Climat
async def async_set_swing_horizontal_mode(self, swing_horizontal_mode: str) -> None:
await self._command({"swing_horizontal": swing_horizontal_mode == SWING_ON})
class GreeControllerZoneClimate(CoordinatorEntity[GreeControllerCoordinator], ClimateEntity):
"""Full climate entity for a controller thermostat zone."""
_attr_has_entity_name = True
_attr_name = None
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_min_temp = 8.0
_attr_max_temp = 30.0
_attr_target_temperature_step = 0.5
_attr_hvac_modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT]
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
def __init__(self, coordinator: GreeControllerCoordinator, zone_id: str) -> None:
super().__init__(coordinator)
self._zone_id = zone_id
self._attr_unique_id = f"{zone_id}-zone-climate"
@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 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 current_temperature(self) -> float | None:
value = self._zone.get("current_temperature")
return float(value) if value is not None else None
@property
def target_temperature(self) -> float | None:
value = self._zone.get("target_temperature")
return float(value) if value is not None else None
@property
def hvac_mode(self) -> HVACMode:
zone = self._zone
if not zone.get("enabled", False):
return HVACMode.OFF
if zone.get("inherit_house_mode", True):
return HVACMode.AUTO
return HVACMode.HEAT if str(zone.get("configured_mode", zone.get("mode", "cool"))) == "heat" else HVACMode.COOL
@property
def extra_state_attributes(self) -> dict[str, Any]:
zone = self._zone
return {
"controller_zone_id": self._zone_id,
"effective_mode": zone.get("mode"),
"follows_house_mode": bool(zone.get("inherit_house_mode", True)),
"preset": zone.get("preset"),
"demand": bool(zone.get("demand", False)),
"device_setpoint": zone.get("device_setpoint"),
"control_source": zone.get("control_source"),
"current_schedule": zone.get("current_schedule_name"),
}
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
if hvac_mode == HVACMode.OFF:
payload = {"enabled": False}
elif hvac_mode == HVACMode.AUTO:
payload = {"enabled": True, "mode": "house"}
elif hvac_mode == HVACMode.COOL:
payload = {"enabled": True, "mode": "cool"}
elif hvac_mode == HVACMode.HEAT:
payload = {"enabled": True, "mode": "heat"}
else:
return
await self.coordinator.client.zone_control(self._zone_id, payload)
await self.coordinator.async_request_refresh()
async def async_set_temperature(self, **kwargs: Any) -> None:
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is None:
return
await self.coordinator.client.zone_control(self._zone_id, {"setpoint": float(temperature)})
await self.coordinator.async_request_refresh()
async def async_turn_on(self) -> None:
await self.coordinator.client.zone_control(self._zone_id, {"enabled": True})
await self.coordinator.async_request_refresh()
async def async_turn_off(self) -> None:
await self.coordinator.client.zone_control(self._zone_id, {"enabled": False})
await self.coordinator.async_request_refresh()