diff --git a/custom_components/dkncloudna/api.py b/custom_components/dkncloudna/api.py index 3935711..10d60b9 100644 --- a/custom_components/dkncloudna/api.py +++ b/custom_components/dkncloudna/api.py @@ -238,12 +238,8 @@ class DknCloudNaClient: payload = {"mac": mac, "property": property_name, "value": value} LOGGER.debug("DKN socket send %s %s", namespace, payload) try: - ack = await socket.call( - "create-machine-event", - payload, - namespace=namespace, - timeout=REQUEST_TIMEOUT, - ) + ack = None + await socket.emit("create-machine-event", payload, namespace=namespace) self._last_command_ack = { "namespace": namespace, "payload": payload, diff --git a/custom_components/dkncloudna/climate.py b/custom_components/dkncloudna/climate.py index b72cce7..c452a4d 100644 --- a/custom_components/dkncloudna/climate.py +++ b/custom_components/dkncloudna/climate.py @@ -39,6 +39,7 @@ from .model import ( supports_swing, target_temperature as model_target_temperature, target_temperature_key, + writable_target_temperature_key, to_device_temperature, ) from .coordinator import DknCoordinator @@ -190,10 +191,16 @@ class DknClimateEntity(DknEntity, ClimateEntity): ) self._optimistic_set("power", False) self._optimistic_set("hvac_mode", HVACMode.OFF) - await self._wait_for_device_value("power", False) else: - await self._ensure_power_on() - await self._ensure_mode_synced(hvac_mode) + mode = _HVAC_TO_MODE.get(hvac_mode) + if mode is None: + raise HomeAssistantError(f"Unsupported HVAC mode: {hvac_mode}") + await self.coordinator.client.async_send_machine_event( + installation_id, self._command_mac, "power", True + ) + await self.coordinator.client.async_send_machine_event( + installation_id, self._command_mac, "mode", mode + ) self._optimistic_set("power", True) self._optimistic_set("hvac_mode", hvac_mode) except Exception as err: # noqa: BLE001 @@ -218,28 +225,27 @@ class DknClimateEntity(DknEntity, ClimateEntity): ) installation_id = self._installation_id - property_name = self._temperature_property_for_mode(target_mode) + property_name = self._writable_temperature_property_for_mode(target_mode) device_temp = self._to_device_temperature(float(temperature)) async with self._get_device_lock(): try: - await self._ensure_power_on() - await self._ensure_mode_synced(target_mode) + requested_mode_code = _HVAC_TO_MODE.get(target_mode) + if ( + requested_mode_code is not None + and self._device_data.get("mode") != requested_mode_code + ): + await self.coordinator.client.async_send_machine_event( + installation_id, self._command_mac, "power", True + ) + await self.coordinator.client.async_send_machine_event( + installation_id, self._command_mac, "mode", requested_mode_code + ) + self._optimistic_set("power", True) + self._optimistic_set("hvac_mode", target_mode) await self.coordinator.client.async_send_machine_event( installation_id, self._command_mac, property_name, device_temp ) - if not await self._wait_for_device_value(property_name, device_temp): - await self.coordinator.async_request_refresh() - await self._ensure_mode_synced(target_mode) - await self.coordinator.client.async_send_machine_event( - installation_id, self._command_mac, property_name, device_temp - ) - if not await self._wait_for_device_value( - property_name, device_temp - ): - raise HomeAssistantError( - f"Device temperature did not sync to {temperature}" - ) except Exception as err: # noqa: BLE001 raise HomeAssistantError(f"Failed to set temperature: {err}") from err @@ -257,7 +263,6 @@ class DknClimateEntity(DknEntity, ClimateEntity): installation_id = self._installation_id async with self._get_device_lock(): try: - await self._ensure_power_on() await self.coordinator.client.async_send_machine_event( installation_id, self._command_mac, "speed_state", speed ) @@ -278,7 +283,6 @@ class DknClimateEntity(DknEntity, ClimateEntity): slat = 9 if swing_mode == "swing" else 0 async with self._get_device_lock(): try: - await self._ensure_power_on() await self.coordinator.client.async_send_machine_event( installation_id, self._command_mac, "slats_vertical_1", slat ) @@ -319,48 +323,11 @@ class DknClimateEntity(DknEntity, ClimateEntity): ) return key + def _writable_temperature_property_for_mode(self, hvac_mode: HVACMode) -> str: + preferred = writable_target_temperature_key(self._device_data) + if preferred is not None: + return preferred + return self._temperature_property_for_mode(hvac_mode) + def _to_device_temperature(self, temperature_c: float) -> float | int: return to_device_temperature(temperature_c, self._device_data.get("units")) - - async def _ensure_mode_synced(self, hvac_mode: HVACMode) -> int: - """Ensure the device mode is synced before dependent writes.""" - mode = _HVAC_TO_MODE.get(hvac_mode) - if mode is None: - raise HomeAssistantError(f"Unsupported HVAC mode: {hvac_mode}") - - await self._ensure_power_on() - - if self._device_data.get("mode") == mode: - return mode - - installation_id = self._installation_id - await self.coordinator.client.async_send_machine_event( - installation_id, self._command_mac, "mode", mode - ) - self._optimistic_set("hvac_mode", hvac_mode) - if not await self._wait_for_device_value("mode", mode): - await self.coordinator.async_request_refresh() - await self.coordinator.client.async_send_machine_event( - installation_id, self._command_mac, "mode", mode - ) - if not await self._wait_for_device_value("mode", mode): - raise HomeAssistantError(f"Device mode did not sync to {hvac_mode}") - return mode - - async def _ensure_power_on(self) -> None: - """Ensure the device is powered on before sending dependent commands.""" - if self._device_data.get("power") is True: - return - - installation_id = self._installation_id - await self.coordinator.client.async_send_machine_event( - installation_id, self._command_mac, "power", True - ) - self._optimistic_set("power", True) - if not await self._wait_for_device_value("power", True): - await self.coordinator.async_request_refresh() - await self.coordinator.client.async_send_machine_event( - installation_id, self._command_mac, "power", True - ) - if not await self._wait_for_device_value("power", True): - raise HomeAssistantError("Device power did not sync to on") diff --git a/custom_components/dkncloudna/model.py b/custom_components/dkncloudna/model.py index 4c36432..1a40ad4 100644 --- a/custom_components/dkncloudna/model.py +++ b/custom_components/dkncloudna/model.py @@ -92,6 +92,21 @@ def target_temperature(data: dict[str, Any]) -> float | None: return to_celsius(data.get(key), data.get("units")) +def writable_target_temperature_key(data: dict[str, Any]) -> str | None: + """Return the setpoint key most likely to be writable for the current state.""" + candidates: list[str] = [] + for mode in (live_mode(data), requested_mode(data), DEVICE_MODE_AUTO): + key = target_temperature_key(mode) + if key is not None and key not in candidates: + candidates.append(key) + + for key in candidates: + if data.get(key) is not None: + return key + + return candidates[0] if candidates else None + + def inferred_hvac_action(data: dict[str, Any]) -> str: """Infer the active HVAC action from requested mode and temperatures.""" power = as_bool(data.get("power")) diff --git a/smoke-test/smoke_test.py b/smoke-test/smoke_test.py index 50f958f..b81148f 100644 --- a/smoke-test/smoke_test.py +++ b/smoke-test/smoke_test.py @@ -58,6 +58,26 @@ def _temp_property(mode: int) -> str | None: return None +def _writable_temp_property(device: dict[str, Any]) -> str | None: + candidates: list[str] = [] + for mode in (device.get("real_mode"), device.get("mode"), 1): + if mode == 1: + key = "setpoint_air_auto" + elif mode == 2: + key = "setpoint_air_cool" + elif mode == 3: + key = "setpoint_air_heat" + else: + key = None + if key is not None and key not in candidates: + candidates.append(key) + + for key in candidates: + if device.get(key) is not None: + return key + return candidates[0] if candidates else None + + def _temp_bounds(device: dict[str, Any], mode: int) -> tuple[Any, Any]: if mode == 1: return device.get("range_sp_auto_air_min"), device.get("range_sp_auto_air_max") @@ -505,7 +525,9 @@ async def _main() -> None: restore_actions.append(("speed_state", original_fan)) mode = int(current_device.get("mode", 1) or 1) - property_name = _temp_property(mode) + property_name = _writable_temp_property(current_device) or _temp_property( + mode + ) if property_name is None: results["writes"]["temperature"] = "skipped_unsupported_mode" else: