Skip to content

Commit

Permalink
Refactor platforms config flow and fixes (#105)
Browse files Browse the repository at this point in the history
* Update translations

* get values from query_mode

* Mirage old config flow to dict

* Update platforms

* update auto configure dict and query model

* Fix: query model will not override code value if it exists.

* Fix birghtness state stuck

* Set timeout for requests fix long start up

* Mirage OFF boolean to set object.

* Fix true value in mirage

* update climates auto configure

* return same value if mirage select fails?

* more fixes for climate mirage

* Update climate auto configure to relay on cloud values

* typo step

* add remap value to cloud value class

* typo modes to actions

* HVAC enums to value
  • Loading branch information
xZetsubou committed Jan 11, 2024
1 parent 842376e commit 2f733ce
Show file tree
Hide file tree
Showing 16 changed files with 687 additions and 638 deletions.
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

0 comments on commit 2f733ce

Please sign in to comment.