v0.5.4
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.5.3",
|
||||
"version": "0.5.4",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
@@ -16,6 +16,17 @@ from .const import DOMAIN
|
||||
from .coordinator import GreeControllerCoordinator
|
||||
|
||||
|
||||
DEVICE_FEATURE_SWITCHES = [
|
||||
("light", "supports_light", "Panel light"),
|
||||
("quiet", "supports_quiet", "Quiet"),
|
||||
("turbo", "supports_turbo", "Turbo"),
|
||||
("xfan", "supports_xfan", "X-FAN"),
|
||||
("air", "supports_air", "Air"),
|
||||
("health", "supports_health", "Health"),
|
||||
("sleep", "supports_sleep", "Sleep"),
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
@@ -23,11 +34,15 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Create one enabled switch per controller zone."""
|
||||
runtime: GreeControllerRuntimeData = entry.runtime_data
|
||||
entities = [
|
||||
entities: list[SwitchEntity] = [
|
||||
GreeControllerZoneEnabledSwitch(runtime.coordinator, str(zone["zone_id"]))
|
||||
for zone in runtime.coordinator.plan.get("zones", [])
|
||||
if zone.get("zone_id")
|
||||
]
|
||||
for device_id, device in runtime.coordinator.data.items():
|
||||
for field, support_field, name in DEVICE_FEATURE_SWITCHES:
|
||||
if device.get(support_field) is True:
|
||||
entities.append(GreeControllerDeviceFeatureSwitch(runtime.coordinator, device_id, field, support_field, name))
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@@ -75,3 +90,61 @@ class GreeControllerZoneEnabledSwitch(CoordinatorEntity[GreeControllerCoordinato
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
await self.coordinator.client.zone_control(self._zone_id, {"enabled": False})
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
class GreeControllerDeviceFeatureSwitch(CoordinatorEntity[GreeControllerCoordinator], SwitchEntity):
|
||||
"""Optional GREE unit feature exposed only when the controller detected it."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: GreeControllerCoordinator,
|
||||
device_id: str,
|
||||
field: str,
|
||||
support_field: str,
|
||||
name: str,
|
||||
) -> None:
|
||||
super().__init__(coordinator)
|
||||
self._device_id = device_id
|
||||
self._field = field
|
||||
self._support_field = support_field
|
||||
self._attr_name = name
|
||||
self._attr_unique_id = f"{device_id}-{field}"
|
||||
|
||||
@property
|
||||
def _device(self) -> dict[str, Any]:
|
||||
return self.coordinator.data.get(self._device_id, {})
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
return (
|
||||
super().available
|
||||
and bool(self._device.get("online", False))
|
||||
and self._device.get(self._support_field) is True
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
return bool(self._device.get(self._field, False))
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
device = self._device
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
name=str(device.get("name") or self._device_id),
|
||||
manufacturer="GREE",
|
||||
model=str(device.get("model") or "GREE HVAC"),
|
||||
sw_version=str(device.get("firmware") or "") or None,
|
||||
)
|
||||
|
||||
async def _set(self, value: bool) -> None:
|
||||
await self.coordinator.client.command(self._device_id, {self._field: value})
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
await self._set(True)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
await self._set(False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user