Skip to content

Commit

Permalink
Add platform support for humidifiers and Rename DPS, and DPS_CONF Fun…
Browse files Browse the repository at this point in the history
…ctions (#47)

* Add support for humidifier platform

* Add Humidifer to Auto Configure feature.

* Improve English translations to be more user friendly #45 by @codyc1515
  • Loading branch information
xZetsubou authored Oct 25, 2023
1 parent 9ee17b8 commit 644c7dc
Show file tree
Hide file tree
Showing 22 changed files with 342 additions and 173 deletions.
2 changes: 1 addition & 1 deletion custom_components/localtuya/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def status_updated(self):
"""Device status was updated."""
super().status_updated()

state = str(self.dps(self._dp_id)).lower()
state = str(self.dp_value(self._dp_id)).lower()
if state == self._config[CONF_STATE_ON].lower() or state == "true":
self._is_on = True
else:
Expand Down
18 changes: 9 additions & 9 deletions custom_components/localtuya/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ async def async_set_preset_mode(self, preset_mode):
def min_temp(self):
"""Return the minimum temperature."""
if _min_temp := self._config.get(CONF_MIN_TEMP_DP):
return self.dps_conf(CONF_MIN_TEMP_DP)
return self.dp_value_conf(CONF_MIN_TEMP_DP)
# DEFAULT_MIN_TEMP is in C
if self.temperature_unit == TEMP_FAHRENHEIT:
return DEFAULT_MIN_TEMP * 1.8 + 32
Expand All @@ -386,7 +386,7 @@ def min_temp(self):
def max_temp(self):
"""Return the maximum temperature."""
if self.has_config(CONF_MAX_TEMP_DP):
return self.dps_conf(CONF_MAX_TEMP_DP)
return self.dp_value_conf(CONF_MAX_TEMP_DP)
# DEFAULT_MAX_TEMP is in C
if self.temperature_unit == TEMP_FAHRENHEIT:
return DEFAULT_MAX_TEMP * 1.8 + 32
Expand All @@ -395,27 +395,27 @@ def max_temp(self):

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

if self.has_config(CONF_TARGET_TEMPERATURE_DP):
self._target_temperature = (
self.dps_conf(CONF_TARGET_TEMPERATURE_DP) * self._target_precision
self.dp_value_conf(CONF_TARGET_TEMPERATURE_DP) * self._target_precision
)

if self.has_config(CONF_CURRENT_TEMPERATURE_DP):
self._current_temperature = (
self.dps_conf(CONF_CURRENT_TEMPERATURE_DP) * self._precision
self.dp_value_conf(CONF_CURRENT_TEMPERATURE_DP) * self._precision
)

if self._has_presets:
if (
self.has_config(CONF_ECO_DP)
and self.dps_conf(CONF_ECO_DP) == self._conf_eco_value
and self.dp_value_conf(CONF_ECO_DP) == self._conf_eco_value
):
self._preset_mode = PRESET_ECO
else:
for preset, value in self._conf_preset_set.items(): # todo remove
if self.dps_conf(CONF_PRESET_DP) == value:
if self.dp_value_conf(CONF_PRESET_DP) == value:
self._preset_mode = preset
break
else:
Expand All @@ -427,7 +427,7 @@ def status_updated(self):
self._hvac_mode = HVACMode.OFF
else:
for mode, value in self._conf_hvac_mode_set.items():
if self.dps_conf(CONF_HVAC_MODE_DP) == value:
if self.dp_value_conf(CONF_HVAC_MODE_DP) == value:
self._hvac_mode = mode
break
else:
Expand All @@ -436,7 +436,7 @@ def status_updated(self):

# Update the current action
for action, value in self._conf_hvac_action_set.items():
if self.dps_conf(CONF_HVAC_ACTION_DP) == value:
if self.dp_value_conf(CONF_HVAC_ACTION_DP) == value:
self._hvac_action = action


Expand Down
16 changes: 6 additions & 10 deletions custom_components/localtuya/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
def __init__(self, device, config_entry, dp_id, logger, **kwargs):
"""Initialize the Tuya entity."""
super().__init__()
self._device = device
self._device: TuyaDevice = device
self._dev_config_entry = config_entry
self._config: dict = get_entity_config(config_entry, dp_id)
self._dp_id = dp_id
Expand Down Expand Up @@ -647,7 +647,7 @@ def device_class(self):
"""Return the class of this device."""
return self._config.get(CONF_DEVICE_CLASS, None)

def dps(self, dp_index):
def dp_value(self, dp_index):
"""Return cached value for DPS index."""
value = self._status.get(str(dp_index))
if value is None and not self._dev_config_entry.get(CONF_NODE_ID):
Expand All @@ -659,27 +659,23 @@ def dps(self, dp_index):

return value

def dps_conf(self, conf_item):
"""Return value of datapoint for user specified config item.
This method looks up which DP a certain config item uses based on
user configuration and returns its value.
"""
def dp_value_conf(self, conf_item):
"""Return DP Value based on config key, If entity has the key."""
dp_index = self._config.get(conf_item)
if dp_index is None:
self.warning(
"Entity %s is requesting unset index for option %s",
self.entity_id,
conf_item,
)
return self.dps(dp_index)
return self.dp_value(dp_index)

def status_updated(self):
"""Device status was updated.
Override in subclasses and update entity specific state.
"""
state = self.dps(self._dp_id)
state = self.dp_value(self._dp_id)
self._state = state

# Keep record in last_state as long as not during connection/re-connection,
Expand Down
1 change: 1 addition & 0 deletions custom_components/localtuya/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"Climate": Platform.CLIMATE,
"Cover": Platform.COVER,
"Fan": Platform.FAN,
"Humidifier": Platform.HUMIDIFIER,
"Light": Platform.LIGHT,
"Number": Platform.NUMBER,
"Selector": Platform.SELECT,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/localtuya/core/tuya_devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
Platform.CLIMATE: CLIMATES,
Platform.COVER: COVERS,
Platform.FAN: FANS,
# Platform.HUMIDIFIER: HUMIDIFIERS,
Platform.HUMIDIFIER: HUMIDIFIERS,
Platform.LIGHT: LIGHTS,
Platform.NUMBER: NUMBERS,
Platform.SELECT: SELECTS,
Expand Down
1 change: 1 addition & 0 deletions custom_components/localtuya/core/tuya_devices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class DPCode(StrEnum):
SOUND_MODE = "sound_mode"
SPEED = "speed" # Speed level
SPRAY_MODE = "spray_mode" # Spraying mode
SPRAY_VOLUME = "spray_volume" # Dehumidifier
START = "start" # Start
STATUS = "status"
STERILIZATION = "sterilization" # Sterilization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,5 @@ def localtuya_binarySensor(state_on="1"):
BINARY_SENSORS["kg"] = FAULT_SENSOR
BINARY_SENSORS["pc"] = FAULT_SENSOR
BINARY_SENSORS["cz"] = FAULT_SENSOR
BINARY_SENSORS["cs"] = FAULT_SENSOR
BINARY_SENSORS["jsq"] = FAULT_SENSOR
30 changes: 22 additions & 8 deletions custom_components/localtuya/core/tuya_devices/humidifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,40 @@
from .base import DPCode, LocalTuyaEntity, CONF_DEVICE_CLASS, EntityCategory
from homeassistant.components.humidifier import HumidifierDeviceClass

CONF_HUMIDIFIER_SET_HUMIDITY_DP = "humidifier_set_humidity_dp"
CONF_HUMIDIFIER_CURRENT_HUMIDITY_DP = "humidifier_current_humidity_dp"
CONF_HUMIDIFIER_MODE_DP = "humidifier_mode_dp"
CONF_HUMIDIFIER_AVAILABLE_MODES = "humidifier_available_modes"


def localtuya_humidifier(modes):
"""Define localtuya fan configs"""
data = {"humidifier_available_modes": modes}
return data


HUMIDIFIERS: dict[LocalTuyaEntity] = {
# Dehumidifier
# https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha
"cs": (
LocalTuyaEntity(
id=(DPCode.SWITCH_SPRAY, DPCode.SWITCH),
# dpcode=(DPCode.SWITCH, DPCode.SWITCH_SPRAY),
current_humidity_dp=DPCode.HUMIDITY_INDOOR,
set_humidity_dp=DPCode.DEHUMIDITY_SET_VALUE,
id=DPCode.SWITCH,
humidifier_current_humidity_dp=DPCode.HUMIDITY_INDOOR,
humidifier_set_humidity_dp=DPCode.DEHUMIDITY_SET_VALUE,
humidifier_mode_dp=(DPCode.MODE, DPCode.WORK_MODE),
custom_configs=localtuya_humidifier("dehumidify,drying,continuous"),
device_class=HumidifierDeviceClass.DEHUMIDIFIER,
)
),
# Humidifier
# https://developer.tuya.com/en/docs/iot/categoryjsq?id=Kaiuz1smr440b
"jsq": (
LocalTuyaEntity(
id=(DPCode.SWITCH_SPRAY, DPCode.SWITCH),
# dpcode=(DPCode.SWITCH, DPCode.SWITCH_SPRAY),
current_humidity_dp=DPCode.HUMIDITY_CURRENT,
set_humidity_dp=DPCode.HUMIDITY_SET,
id=DPCode.SWITCH,
humidifier_current_humidity_dp=DPCode.HUMIDITY_CURRENT,
humidifier_set_humidity_dp=DPCode.HUMIDITY_SET,
humidifier_mode_dp=(DPCode.MODE, DPCode.WORK_MODE),
custom_configs=localtuya_humidifier("large,middle,small"),
device_class=HumidifierDeviceClass.HUMIDIFIER,
)
),
Expand Down
4 changes: 4 additions & 0 deletions custom_components/localtuya/core/tuya_devices/lights.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,7 @@ def localtuya_light(
# Power Socket (duplicate of `kg`)
# https://developer.tuya.com/en/docs/iot/s?id=K9gf7o5prgf7s
LIGHTS["pc"] = LIGHTS["kg"]

# Dehumidifier
# https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha
LIGHTS["cs"] = LIGHTS["jsq"]
11 changes: 8 additions & 3 deletions custom_components/localtuya/core/tuya_devices/selects.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,7 @@ def localtuya_selector(options, options_name=None):
entity_category=EntityCategory.CONFIG,
icon="mdi:timer-cog-outline",
name="Countdown",
custom_configs=localtuya_selector(
"cancel,1,2,3,4,5,6", "Cancel,1H,2H,3H,4H,5H,6H"
),
custom_configs=localtuya_selector("cancel,2h,4h,8h", "Cancel,2H,4H,8H"),
),
LocalTuyaEntity(
id=DPCode.DEHUMIDITY_SET_ENUM,
Expand All @@ -541,6 +539,13 @@ def localtuya_selector(options, options_name=None):
icon="mdi:water-percent",
custom_configs=localtuya_selector("10,20,30,40,50", "10,20,30,40,50"),
),
LocalTuyaEntity(
id=DPCode.SPRAY_VOLUME,
name="Intensity",
entity_category=EntityCategory.CONFIG,
icon="mdi:volume-source",
custom_configs=localtuya_selector("small,middle,large", "Low,Medium,High"),
),
),
# sous vide cookers
# https://developer.tuya.com/en/docs/iot/f?id=K9r2v9hgmyk3h
Expand Down
14 changes: 11 additions & 3 deletions custom_components/localtuya/core/tuya_devices/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,10 @@ def localtuya_sensor(unit_of_measurement=None, scale_factor: float = None) -> di
),
LocalTuyaEntity(
id=DPCode.REMAIN_TIME,
# translation_id="remaining_time",
name="Timer Remaining",
custom_configs=localtuya_sensor(UnitOfTime.MINUTES),
icon="mdi:timer",
entity_category=EntityCategory.DIAGNOSTIC,
),
),
# PIR Detector
Expand Down Expand Up @@ -1030,16 +1031,23 @@ def localtuya_sensor(unit_of_measurement=None, scale_factor: float = None) -> di
"cs": (
LocalTuyaEntity(
id=DPCode.TEMP_INDOOR,
# translation_id="temperature",
name="Temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
LocalTuyaEntity(
id=DPCode.HUMIDITY_INDOOR,
# translation_id="humidity",
name="Humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
LocalTuyaEntity(
id=DPCode.COUNTDOWN_LEFT,
translation_id="Timer Remaining",
custom_configs=localtuya_sensor(UnitOfTime.MINUTES),
icon="mdi:timer",
entity_category=EntityCategory.DIAGNOSTIC,
),
),
# Soil sensor (Plant monitor)
"zwjcy": (
Expand Down
10 changes: 10 additions & 0 deletions custom_components/localtuya/core/tuya_devices/switches.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,12 @@
icon="mdi:minus-circle-outline",
entity_category=EntityCategory.CONFIG,
),
LocalTuyaEntity(
id=DPCode.SWITCH_SPRAY,
name="Spray",
icon="mdi:spray",
entity_category=EntityCategory.CONFIG,
),
),
}

Expand All @@ -728,3 +734,7 @@
SWITCHES["cz"] = SWITCHES["pc"]

SWITCHES["wk"] = CHILD_LOCK

# Dehumidifier
# https://developer.tuya.com/en/docs/iot/categorycs?id=Kaiuz1vcz4dha
SWITCHES["cs"] = SWITCHES["jsq"]
4 changes: 2 additions & 2 deletions custom_components/localtuya/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,14 @@ def status_restored(self, stored_state):
def status_updated(self):
"""Device status was updated."""
self._previous_state = self._state
self._state = self.dps(self._dp_id)
self._state = self.dp_value(self._dp_id)
if self._state.isupper():
self._open_cmd = self._open_cmd.upper()
self._close_cmd = self._close_cmd.upper()
self._stop_cmd = self._stop_cmd.upper()

if self.has_config(CONF_CURRENT_POSITION_DP):
curr_pos = self.dps_conf(CONF_CURRENT_POSITION_DP)
curr_pos = self.dp_value_conf(CONF_CURRENT_POSITION_DP)
if self._config.get(CONF_POSITION_INVERTED):
self._current_cover_position = 100 - curr_pos
else:
Expand Down
8 changes: 4 additions & 4 deletions custom_components/localtuya/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ def speed_count(self) -> int:

def status_updated(self):
"""Get state of Tuya fan."""
self._is_on = self.dps(self._dp_id)
self._is_on = self.dp_value(self._dp_id)

current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL)
current_speed = self.dp_value_conf(CONF_FAN_SPEED_CONTROL)
if self._use_ordered_list:
_LOGGER.debug(
"Fan current_speed ordered_list_item_to_percentage: %s from %s",
Expand All @@ -242,11 +242,11 @@ def status_updated(self):
_LOGGER.debug("Fan current_percentage: %s", self._percentage)

if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL)
self._oscillating = self.dp_value_conf(CONF_FAN_OSCILLATING_CONTROL)
_LOGGER.debug("Fan current_oscillating : %s", self._oscillating)

if self.has_config(CONF_FAN_DIRECTION):
value = self.dps_conf(CONF_FAN_DIRECTION)
value = self.dp_value_conf(CONF_FAN_DIRECTION)
if value is not None:
if value == self._config.get(CONF_FAN_DIRECTION_FWD):
self._direction = DIRECTION_FORWARD
Expand Down
Loading

0 comments on commit 644c7dc

Please sign in to comment.