Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor platforms config flow and fixes #105

Merged
merged 18 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions custom_components/localtuya/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,84 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
i = i + 1
config_entry.version = new_version
hass.config_entries.async_update_entry(config_entry, data=new_data)
if config_entry.version <= 3:
# Convert values and friendly name values to dict.
from .const import (
Platform,
CONF_OPTIONS,
CONF_HVAC_MODE_SET,
CONF_HVAC_ACTION_SET,
CONF_PRESET_SET,
CONF_SCENE_VALUES,
# Deprecated
CONF_SCENE_VALUES_FRIENDLY,
CONF_OPTIONS_FRIENDLY,
CONF_HVAC_ADD_OFF,
)
from .climate import (
RENAME_HVAC_MODE_SETS,
RENAME_ACTION_SETS,
RENAME_PRESET_SETS,
HVAC_OFF,
)

def convert_str_to_dict(list1: str, list2: str = ""):
to_dict = {}
if not isinstance(list1, str):
return list1
list1, list2 = list1.replace(";", ","), list2.replace(";", ",")
v, v_fn = list1.split(","), list2.split(",")
for k in range(len(v)):
to_dict[v[k]] = (
v_fn[k] if k < len(v_fn) and v_fn[k] else v[k].capitalize()
)
return to_dict

new_data = config_entry.data.copy()
for device in new_data[CONF_DEVICES]:
current_entity = 0
for entity in new_data[CONF_DEVICES][device][CONF_ENTITIES]:
new_entity_data = {}
if entity[CONF_PLATFORM] == Platform.SELECT:
# Merge 2 Lists Values and Values friendly names into dict.
v_fn = entity.get(CONF_OPTIONS_FRIENDLY, "")
if v := entity.get(CONF_OPTIONS):
new_entity_data[CONF_OPTIONS] = convert_str_to_dict(v, v_fn)
if entity[CONF_PLATFORM] == Platform.LIGHT:
v_fn = entity.get(CONF_SCENE_VALUES_FRIENDLY, "")
if v := entity.get(CONF_SCENE_VALUES):
new_entity_data[CONF_SCENE_VALUES] = convert_str_to_dict(
v, v_fn
)
if entity[CONF_PLATFORM] == Platform.CLIMATE:
# Merge 2 Lists Values and Values friendly names into dict.
climate_to_dict = {}
for conf, new_values in (
(CONF_HVAC_MODE_SET, RENAME_HVAC_MODE_SETS),
(CONF_HVAC_ACTION_SET, RENAME_ACTION_SETS),
(CONF_PRESET_SET, RENAME_PRESET_SETS),
):
climate_to_dict[conf] = {}
if hvac_set := entity.get(conf, ""):
if entity.get(CONF_HVAC_ADD_OFF, False):
if conf == CONF_HVAC_MODE_SET:
climate_to_dict[conf].update(HVAC_OFF)
if not isinstance(conf, str):
continue
hvac_set = hvac_set.replace("/", ",")
for i in hvac_set.split(","):
for k, v in new_values.items():
if i in k:
new_v = True if i == "True" else i
new_v = False if i == "False" else new_v
climate_to_dict[conf].update({v: new_v})
new_entity_data = climate_to_dict
new_data[CONF_DEVICES][device][CONF_ENTITIES][current_entity].update(
new_entity_data
)
current_entity += 1
config_entry.version = new_version
hass.config_entries.async_update_entry(config_entry, data=new_data)

_LOGGER.info(
"Entry %s successfully migrated to version %s.",
Expand Down
181 changes: 64 additions & 117 deletions custom_components/localtuya/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
from functools import partial
from .config_flow import _col_to_select
from homeassistant.helpers import selector

import voluptuous as vol
from homeassistant.components.climate import (
Expand Down Expand Up @@ -54,93 +55,49 @@

_LOGGER = logging.getLogger(__name__)


HVAC_OFF = {HVACMode.OFF.value: "off"}
RENAME_HVAC_MODE_SETS = { # Mirgae to 3
("manual", "Manual", "hot", "m", "True"): HVACMode.HEAT.value,
("auto", "0", "p", "Program"): HVACMode.AUTO.value,
("freeze", "cold", "1"): HVACMode.COOL.value,
("wet"): HVACMode.DRY.value,
}
RENAME_ACTION_SETS = { # Mirgae to 3
("open", "opened", "heating", "Heat", "True"): HVACAction.HEATING.value,
("closed", "close", "no_heating"): HVACAction.IDLE.value,
("Warming", "warming", "False"): HVACAction.IDLE.value,
("cooling"): HVACAction.COOLING.value,
("off"): HVACAction.OFF.value,
}
RENAME_PRESET_SETS = {
"Holiday": (PRESET_AWAY),
"Program": (PRESET_HOME),
"Manual": (PRESET_NONE, "manual"),
"Auto": "auto",
"Manual": "manual",
"Smart": "smart",
"Comfort": "comfortable",
"ECO": "eco",
}


HVAC_MODE_SETS = {
"manual/auto": {
HVACMode.HEAT: "manual",
HVACMode.AUTO: "auto",
},
"Manual/Program": {
HVACMode.HEAT: "Manual",
HVACMode.AUTO: "Program",
},
"freeze/manual/auto": {
HVACMode.COOL: "freeze",
HVACMode.HEAT: "manual",
HVACMode.AUTO: "auto",
},
"auto/cold/hot/wet": {
HVACMode.AUTO: "auto",
HVACMode.COOL: "cold",
HVACMode.HEAT: "hot",
HVACMode.DRY: "wet",
},
"m/p": {
HVACMode.HEAT: "m",
HVACMode.AUTO: "p",
},
"True/False": {
HVACMode.HEAT: True,
},
"1/0": {
HVACMode.HEAT: "1",
HVACMode.AUTO: "0",
},
"smart/auto": {
HVACMode.HEAT_COOL: "1",
HVACMode.AUTO: "auto",
},
HVACMode.OFF: False,
HVACMode.AUTO: "auto",
HVACMode.COOL: "cold",
HVACMode.HEAT: "hot",
HVACMode.HEAT_COOL: "heat",
HVACMode.DRY: "wet",
HVACMode.FAN_ONLY: "wind",
}

HVAC_ACTION_SETS = {
"True/False": {
HVACAction.HEATING: True,
HVACAction.IDLE: False,
},
"open/close": {
HVACAction.HEATING: "open",
HVACAction.IDLE: "close",
},
"opened/closed": {
HVACAction.HEATING: "opened",
HVACAction.IDLE: "closed",
},
"heating/no_heating": {
HVACAction.HEATING: "heating",
HVACAction.IDLE: "no_heating",
},
"heating/cooling": {
HVACAction.HEATING: "heating",
HVACAction.COOLING: "cooling",
HVACAction.IDLE: "ventilation",
HVACAction.OFF: "off",
},
"Heat/Warming": {
HVACAction.HEATING: "Heat",
HVACAction.IDLE: "Warming",
},
"heating/warming": {
HVACAction.HEATING: "heating",
HVACAction.IDLE: "warming",
},
}
PRESET_SETS = {
"Manual/Holiday/Program": {
PRESET_AWAY: "Holiday",
PRESET_HOME: "Program",
PRESET_NONE: "Manual",
},
"auto/smart": {
"auto": "Auto",
"smart": "Smart",
},
"auto/manual/smart/comfortable/eco": {
"auto": "Auto",
"manual": "Manual",
"smart": "Smart",
"comfortable": "Comfort",
"eco": "ECO",
},
HVACAction.HEATING: "opened",
HVACAction.IDLE: "closed",
}


TEMPERATURE_CELSIUS = "celsius"
TEMPERATURE_FAHRENHEIT = "fahrenheit"
DEFAULT_TEMPERATURE_UNIT = TEMPERATURE_CELSIUS
Expand All @@ -160,26 +117,24 @@ def flow_schema(dps):
),
vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float),
vol.Optional(CONF_PRECISION): _col_to_select(
vol.Optional(CONF_PRECISION, default=str(DEFAULT_PRECISION)): _col_to_select(
[PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
),
vol.Optional(CONF_HVAC_MODE_DP): _col_to_select(dps, is_dps=True),
vol.Optional(CONF_HVAC_MODE_SET): _col_to_select(list(HVAC_MODE_SETS.keys())),
vol.Optional(
CONF_HVAC_MODE_SET, default=HVAC_MODE_SETS
): selector.ObjectSelector(),
vol.Optional(CONF_HVAC_ACTION_DP): _col_to_select(dps, is_dps=True),
vol.Optional(CONF_HVAC_ACTION_SET): _col_to_select(
list(HVAC_ACTION_SETS.keys())
),
vol.Optional(
CONF_HVAC_ACTION_SET, default=HVAC_ACTION_SETS
): selector.ObjectSelector(),
vol.Optional(CONF_ECO_DP): _col_to_select(dps, is_dps=True),
vol.Optional(CONF_ECO_VALUE): str,
vol.Optional(CONF_PRESET_DP): _col_to_select(dps, is_dps=True),
vol.Optional(CONF_PRESET_SET): _col_to_select(list(PRESET_SETS.keys())),
vol.Optional(CONF_PRESET_SET, default={}): selector.ObjectSelector(),
vol.Optional(CONF_TEMPERATURE_UNIT): _col_to_select(
[TEMPERATURE_CELSIUS, TEMPERATURE_FAHRENHEIT]
),
vol.Optional(CONF_TARGET_PRECISION): _col_to_select(
[PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
),
vol.Optional(CONF_HVAC_ADD_OFF, default=True): bool,
vol.Optional(CONF_HEURISTIC_ACTION): bool,
}

Expand All @@ -203,19 +158,17 @@ def __init__(
self._preset_mode = None
self._hvac_action = None
self._precision = float(self._config.get(CONF_PRECISION, DEFAULT_PRECISION))
self._target_precision = float(
self._config.get(CONF_TARGET_PRECISION, self._precision)
)
self._conf_hvac_mode_dp = self._config.get(CONF_HVAC_MODE_DP)
self._conf_hvac_mode_set = HVAC_MODE_SETS.get(
self._config.get(CONF_HVAC_MODE_SET), {}
)
if modes_set := self._config.get(CONF_HVAC_MODE_SET, {}):
# HA HVAC Modes are all lower case.
modes_set = {k.lower(): v for k, v in modes_set.copy().items()}
self._conf_hvac_mode_set = modes_set
self._conf_preset_dp = self._config.get(CONF_PRESET_DP)
self._conf_preset_set = PRESET_SETS.get(self._config.get(CONF_PRESET_SET), {})
self._conf_preset_set: dict = self._config.get(CONF_PRESET_SET, {})
self._conf_hvac_action_dp = self._config.get(CONF_HVAC_ACTION_DP)
self._conf_hvac_action_set = HVAC_ACTION_SETS.get(
self._config.get(CONF_HVAC_ACTION_SET), {}
)
if actions_set := self._config.get(CONF_HVAC_ACTION_SET, {}):
actions_set = {k.lower(): v for k, v in actions_set.copy().items()}
self._conf_hvac_action_set = actions_set
self._conf_eco_dp = self._config.get(CONF_ECO_DP)
self._conf_eco_value = self._config.get(CONF_ECO_VALUE, "ECO")
self._has_presets = self.has_config(CONF_ECO_DP) or self.has_config(
Expand All @@ -225,7 +178,7 @@ def __init__(
@property
def supported_features(self):
"""Flag supported features."""
supported_features = 0
supported_features = ClimateEntityFeature(0)
if self.has_config(CONF_TARGET_TEMPERATURE_DP):
supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
if self.has_config(CONF_PRESET_DP) or self.has_config(CONF_ECO_DP):
Expand All @@ -237,11 +190,6 @@ def precision(self):
"""Return the precision of the system."""
return self._precision

@property
def target_precision(self):
"""Return the precision of the target."""
return self._target_precision

@property
def temperature_unit(self):
"""Return the unit of measurement used by the platform."""
Expand Down Expand Up @@ -313,7 +261,7 @@ def preset_modes(self):
"""Return the list of available presets modes."""
if not self._has_presets:
return None
presets = list(self._conf_preset_set)
presets = list(self._conf_preset_set.values())
if self._conf_eco_dp:
presets.append(PRESET_ECO)
return presets
Expand Down Expand Up @@ -346,7 +294,7 @@ def fan_modes(self):
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
if ATTR_TEMPERATURE in kwargs and self.has_config(CONF_TARGET_TEMPERATURE_DP):
temperature = round(kwargs[ATTR_TEMPERATURE] / self._target_precision)
temperature = round(kwargs[ATTR_TEMPERATURE] / self.precision)
await self._device.set_dp(
temperature, self._config[CONF_TARGET_TEMPERATURE_DP]
)
Expand All @@ -364,6 +312,7 @@ async def async_set_hvac_mode(self, hvac_mode):
await self._device.set_dp(True, self._dp_id)
# Some thermostats need a small wait before sending another update
await asyncio.sleep(MODE_WAIT)

await self._device.set_dp(
self._conf_hvac_mode_set[hvac_mode], self._conf_hvac_mode_dp
)
Expand All @@ -381,17 +330,15 @@ async def async_set_preset_mode(self, preset_mode):
if preset_mode == PRESET_ECO:
await self._device.set_dp(self._conf_eco_value, self._conf_eco_dp)
return
await self._device.set_dp(
self._conf_preset_set[preset_mode], self._conf_preset_dp
)
await self._device.set_dp(preset_mode, self._conf_preset_dp)

def status_updated(self):
"""Device status was updated."""
self._state = self.dp_value(self._dp_id)

if self.has_config(CONF_TARGET_TEMPERATURE_DP):
self._target_temperature = (
self.dp_value(CONF_TARGET_TEMPERATURE_DP) * self._target_precision
self.dp_value(CONF_TARGET_TEMPERATURE_DP) * self._precision
)

if self.has_config(CONF_CURRENT_TEMPERATURE_DP):
Expand All @@ -406,9 +353,9 @@ def status_updated(self):
):
self._preset_mode = PRESET_ECO
else:
for preset, value in self._conf_preset_set.items(): # todo remove
if self.dp_value(CONF_PRESET_DP) == value:
self._preset_mode = preset
for preset_value, preset_name in self._conf_preset_set.items():
if self.dp_value(CONF_PRESET_DP) == preset_value:
self._preset_mode = preset_name
break
else:
self._preset_mode = PRESET_NONE
Expand Down