homeassistant/custom_components/sonoff/climate.py
2025-01-10 21:08:35 -08:00

286 lines
10 KiB
Python

from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
ClimateEntityFeature,
HVACMode,
)
from homeassistant.const import UnitOfTemperature
from .core.const import DOMAIN
from .core.entity import XEntity
from .core.ewelink import SIGNAL_ADD_ENTITIES, XRegistry
PARALLEL_UPDATES = 0 # fix entity_platform parallel_updates Semaphore
async def async_setup_entry(hass, config_entry, add_entities):
ewelink: XRegistry = hass.data[DOMAIN][config_entry.entry_id]
ewelink.dispatcher_connect(
SIGNAL_ADD_ENTITIES,
lambda x: add_entities([e for e in x if isinstance(e, ClimateEntity)]),
)
# noinspection PyAbstractClass
class XClimateTH(XEntity, ClimateEntity):
params = {"targets", "deviceType", "currentTemperature", "temperature"}
_attr_entity_registry_enabled_default = False
_attr_hvac_mode = None
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL, HVACMode.DRY]
_attr_max_temp = 99
_attr_min_temp = 1
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
_attr_target_temperature_high = None
_attr_target_temperature_low = None
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = 1
heat: bool = None
def set_state(self, params: dict):
if "targets" in params:
hi, lo = params["targets"]
self._attr_is_aux_heat = lo["reaction"]["switch"] == "on"
self._attr_target_temperature_high = float(hi["targetHigh"])
self._attr_target_temperature_low = float(lo["targetLow"])
if params["deviceType"] == "normal":
self._attr_hvac_mode = HVACMode.OFF
elif params["deviceType"] == "humidity":
self._attr_hvac_mode = HVACMode.DRY
elif self.is_aux_heat:
self._attr_hvac_mode = HVACMode.HEAT
else:
self._attr_hvac_mode = HVACMode.COOL
try:
if self.hvac_mode != HVACMode.DRY:
value = float(params.get("currentTemperature") or params["temperature"])
value = round(value, 1)
else:
value = int(params.get("currentHumidity") or params["humidity"])
self._attr_current_temperature = value
except Exception:
pass
def get_targets(self, heat: bool) -> list:
return [
{
"targetHigh": str(self.target_temperature_high),
"reaction": {"switch": "off" if heat else "on"},
},
{
"targetLow": str(self.target_temperature_low),
"reaction": {"switch": "on" if heat else "off"},
},
]
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
if hvac_mode == HVACMode.HEAT:
params = {
"mainSwitch": "on",
"deviceType": "temperature",
"targets": self.get_targets(True),
}
elif hvac_mode == HVACMode.COOL:
params = {
"mainSwitch": "on",
"deviceType": "temperature",
"targets": self.get_targets(False),
}
elif hvac_mode == HVACMode.DRY:
params = {
"mainSwitch": "on",
"deviceType": "humidity",
"targets": self.get_targets(self.is_aux_heat),
}
else:
params = {"mainSwitch": "off", "deviceType": "normal"}
await self.ewelink.send_cloud(self.device, params)
async def async_set_temperature(
self,
hvac_mode: str = None,
target_temp_high: float = None,
target_temp_low: float = None,
**kwargs
) -> None:
heat = self.is_aux_heat
if hvac_mode is None:
params = {}
elif hvac_mode == HVACMode.HEAT:
heat = True
params = {"mainSwitch": "on", "deviceType": "temperature"}
elif hvac_mode == HVACMode.COOL:
heat = False
params = {"mainSwitch": "on", "deviceType": "temperature"}
elif hvac_mode == HVACMode.DRY:
params = {"mainSwitch": "on", "deviceType": "humidity"}
else:
params = {"mainSwitch": "off", "deviceType": "normal"}
if target_temp_high is not None and target_temp_low is not None:
params["targets"] = [
{
"targetHigh": str(target_temp_high),
"reaction": {"switch": "off" if heat else "on"},
},
{
"targetLow": str(target_temp_low),
"reaction": {"switch": "on" if heat else "off"},
},
]
await self.ewelink.send_cloud(self.device, params)
# noinspection PyAbstractClass
class XClimateNS(XEntity, ClimateEntity):
params = {"ATCEnable", "ATCMode", "temperature", "tempCorrection"}
_attr_entity_registry_enabled_default = False
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT_COOL, HVACMode.AUTO]
_attr_max_temp = 31
_attr_min_temp = 16
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = 1
def set_state(self, params: dict):
cache = self.device["params"]
if cache != params:
cache.update(params)
if "HMI_ATCDevice" in params and "etype" in params["HMI_ATCDevice"]:
if cache["HMI_ATCDevice"]["etype"] == "cold":
self._attr_hvac_modes[1] = HVACMode.COOL
else:
self._attr_hvac_modes[1] = HVACMode.HEAT
if "ATCEnable" in params or "ATCMode" in params:
if cache["ATCEnable"]:
if cache["ATCMode"]:
self.set_hvac_attr(HVACMode.AUTO)
else:
self.set_hvac_attr(self._attr_hvac_modes[1])
else:
self.set_hvac_attr(HVACMode.OFF)
if "ATCExpect0" in params:
self._attr_target_temperature = cache["ATCExpect0"]
# correction could be optional
# https://github.com/AlexxIT/SonoffLAN/issues/812
if "temperature" in params or "tempCorrection" in params:
try:
# https://github.com/AlexxIT/SonoffLAN/issues/1100
self._attr_current_temperature = cache["temperature"]
self._attr_current_temperature += cache.get("tempCorrection", 0)
except:
pass
def set_hvac_attr(self, hvac_mode: str) -> None:
if hvac_mode == HVACMode.AUTO:
self._attr_hvac_mode = hvac_mode
self._attr_supported_features = 0
elif hvac_mode == HVACMode.OFF:
self._attr_hvac_mode = hvac_mode
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
elif hvac_mode in (HVACMode.COOL, HVACMode.HEAT, HVACMode.HEAT_COOL):
self._attr_hvac_mode = self._attr_hvac_modes[1]
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
@staticmethod
def get_params(hvac_mode: str) -> dict:
if hvac_mode == HVACMode.AUTO:
return {"ATCEnable": 1, "ATCMode": 1}
elif hvac_mode in (HVACMode.COOL, HVACMode.HEAT):
return {"ATCEnable": 1, "ATCMode": 0}
elif hvac_mode == HVACMode.HEAT_COOL:
return {"ATCEnable": 1} # async_turn_on
elif hvac_mode == HVACMode.OFF:
return {"ATCEnable": 0}
else:
return {}
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
params = self.get_params(hvac_mode)
await self.ewelink.send_cloud(self.device, params)
async def async_set_temperature(
self, temperature: float = None, hvac_mode: str = None, **kwargs
) -> None:
if temperature is not None:
# https://github.com/AlexxIT/SonoffLAN/issues/1107
params = {"ATCMode": 0, "ATCExpect0": temperature}
elif hvac_mode is not None:
params = self.get_params(hvac_mode)
else:
params = {"ATCEnable": 1}
await self.ewelink.send_cloud(self.device, params)
# noinspection PyAbstractClass
class XThermostat(XEntity, ClimateEntity):
params = {"switch", "targetTemp", "temperature", "workMode", "workState"}
# @bwp91 https://github.com/AlexxIT/SonoffLAN/issues/358
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT, HVACMode.AUTO]
_attr_max_temp = 45
_attr_min_temp = 5
_attr_preset_modes = ["manual", "programmed", "economical"]
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_target_temperature_step = 0.5
def set_state(self, params: dict):
cache = self.device["params"]
if cache != params:
cache.update(params)
if cache["switch"] == "on":
# workState: 1=heating, 2=auto
self._attr_hvac_mode = self.hvac_modes[cache["workState"]]
else:
self._attr_hvac_mode = HVACMode.OFF
if "workMode" in params:
self._attr_preset_mode = self.preset_modes[params["workMode"] - 1]
if "targetTemp" in params:
self._attr_target_temperature = params["targetTemp"]
if "temperature" in params:
self._attr_current_temperature = params["temperature"]
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
i = self.hvac_modes.index(hvac_mode)
params = {"switch": "on", "workState": i} if i else {"switch": "off"}
await self.ewelink.send(self.device, params)
async def async_set_preset_mode(self, preset_mode: str) -> None:
i = self.preset_modes.index(preset_mode) + 1
await self.ewelink.send(self.device, {"workMode": i})
async def async_set_temperature(
self,
temperature: float = None,
hvac_mode: str = None,
preset_mode: str = None,
**kwargs
) -> None:
if hvac_mode is None:
params = {}
elif hvac_mode is HVACMode.OFF:
params = {"switch": "off"}
else:
i = self.hvac_modes.index(hvac_mode)
params = {"switch": "on", "workState": i}
if preset_mode is not None:
params["workMode"] = self.preset_modes.index(preset_mode) + 1
if temperature is not None:
params["targetTemp"] = temperature
await self.ewelink.send(self.device, params)