Skip to content

Commit 1cbaf31

Browse files
committed
[fix] pir, ywbj, sj
[fix] login problem [add] battery state [add] Air Purifier, Smart RGB Plug [update] tuya-iot-python-sdk to 0.3.1
1 parent 40d7829 commit 1cbaf31

11 files changed

+211
-92
lines changed

custom_components/tuya_v2/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
TUYA_DISCOVERY_NEW,
3737
TUYA_HA_DEVICES,
3838
TUYA_HA_TUYA_MAP,
39+
TUYA_HOME_MANAGER,
3940
TUYA_MQTT_LISTENER,
4041
TUYA_SETUP_PLATFORM,
4142
TUYA_SUPPORT_HA_TYPE,
@@ -104,6 +105,7 @@ async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool:
104105
# Get device list
105106
home_manager = TuyaHomeManager(api, tuya_mq, device_manager)
106107
await hass.async_add_executor_job(home_manager.update_device_cache)
108+
hass.data[DOMAIN][TUYA_HOME_MANAGER] = home_manager
107109

108110
class DeviceListener(TuyaDeviceListener):
109111
"""Device Update Listener."""

custom_components/tuya_v2/binary_sensor.py

+35-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
DEVICE_CLASS_PROBLEM,
1515
DEVICE_CLASS_SMOKE,
1616
DEVICE_CLASS_LOCK,
17+
DEVICE_CLASS_BATTERY,
1718
DOMAIN as DEVICE_DOMAIN,
1819
BinarySensorEntity,
1920
)
@@ -48,14 +49,18 @@
4849

4950
DPCODE_SWITCH = "switch"
5051

52+
53+
DPCODE_BATTERY_STATE = "battery_state"
54+
5155
DPCODE_DOORCONTACT_STATE = "doorcontact_state"
5256
DPCODE_SMOKE_SENSOR_STATE = "smoke_sensor_state"
57+
DPCODE_SMOKE_SENSOR_STATUS = "smoke_sensor_status"
5358
DPCODE_GAS_SENSOR_STATE = "gas_sensor_state"
5459
DPCODE_PIR = "pir"
5560
DPCODE_WATER_SENSOR_STATE = "watersensor_state"
5661
DPCODE_SOS_STATE = "sos_state"
5762
DPCODE_PRESENCE_STATE = "presence_state"
58-
63+
DPCODE_TEMPER_ALRAM = "temper_alarm"
5964
DPCODE_DOORLOCK_STATE = "closed_opened"
6065

6166

@@ -133,6 +138,33 @@ def _setup_entities(hass, device_ids: List):
133138
(lambda d: d.status.get(DPCODE_SMOKE_SENSOR_STATE, 1) == "1"),
134139
)
135140
)
141+
if DPCODE_SMOKE_SENSOR_STATUS in device.status:
142+
entities.append(
143+
TuyaHaBSensor(
144+
device,
145+
device_manager,
146+
DEVICE_CLASS_SMOKE,
147+
(lambda d: d.status.get(DPCODE_SMOKE_SENSOR_STATUS, 'normal') == "alarm"),
148+
)
149+
)
150+
if DPCODE_BATTERY_STATE in device.status:
151+
entities.append(
152+
TuyaHaBSensor(
153+
device,
154+
device_manager,
155+
DEVICE_CLASS_BATTERY,
156+
(lambda d: d.status.get(DPCODE_BATTERY_STATE, 'normal') == "low"),
157+
)
158+
)
159+
if DPCODE_TEMPER_ALRAM in device.status:
160+
entities.append(
161+
TuyaHaBSensor(
162+
device,
163+
device_manager,
164+
DEVICE_CLASS_MOTION,
165+
(lambda d: d.status.get(DPCODE_TEMPER_ALRAM, False)),
166+
)
167+
)
136168
if DPCODE_GAS_SENSOR_STATE in device.status:
137169
entities.append(
138170
TuyaHaBSensor(
@@ -148,7 +180,7 @@ def _setup_entities(hass, device_ids: List):
148180
device,
149181
device_manager,
150182
DEVICE_CLASS_MOTION,
151-
(lambda d: d.status.get(DPCODE_PIR, "none") == "1"),
183+
(lambda d: d.status.get(DPCODE_PIR, "none") == "pir"),
152184
)
153185
)
154186
if DPCODE_WATER_SENSOR_STATE in device.status:
@@ -157,7 +189,7 @@ def _setup_entities(hass, device_ids: List):
157189
device,
158190
device_manager,
159191
DEVICE_CLASS_MOISTURE,
160-
(lambda d: d.status.get(DPCODE_WATER_SENSOR_STATE, "none") == "1"),
192+
(lambda d: d.status.get(DPCODE_WATER_SENSOR_STATE, "normal") == "alarm"),
161193
)
162194
)
163195
if DPCODE_SOS_STATE in device.status:

custom_components/tuya_v2/config_flow.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,14 @@ def _try_login(cls, user_input):
8181
if project_type == ProjectType.INDUSTY_SOLUTIONS:
8282
response = api.login(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
8383
else:
84-
for endpoint in TUYA_ENDPOINT.keys():
85-
api.endpoint = endpoint
86-
response = api.login(user_input[CONF_USERNAME],
87-
user_input[CONF_PASSWORD],
88-
user_input[CONF_COUNTRY_CODE],
89-
user_input[CONF_APP_TYPE])
90-
if response.get("success", False):
91-
user_input[CONF_ENDPOINT] = endpoint
92-
break
84+
api.endpoint = "https://openapi.tuyacn.com"
85+
response = api.login(user_input[CONF_USERNAME],
86+
user_input[CONF_PASSWORD],
87+
user_input[CONF_COUNTRY_CODE],
88+
user_input[CONF_APP_TYPE])
89+
if response.get("success", False):
90+
api.endpoint = api.token_info.platform_url
91+
user_input[CONF_ENDPOINT] = api.token_info.platform_url
9392

9493
_LOGGER.info(f"TuyaConfigFlow._try_login finish, response:, {response}")
9594
return response

custom_components/tuya_v2/const.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
TUYA_DISCOVERY_NEW = "tuya_v2_discovery_new_{}"
1616
TUYA_DEVICE_MANAGER = "tuya_device_manager"
17+
TUYA_HOME_MANAGER = "tuya_home_manager"
1718
TUYA_MQTT_LISTENER = "tuya_mqtt_listener"
1819
TUYA_HA_TUYA_MAP = "tuya_ha_tuya_map"
1920
TUYA_HA_DEVICES = "tuya_ha_devices"
@@ -43,10 +44,7 @@
4344
"humidifier",
4445
"number",
4546
"vacuum",
46-
"select"
47+
"select",
48+
# "remote"
4749
# 'alarm_control_panel'
4850
]
49-
50-
TUYA_AIR_PURIFIER_TYPE = "kj"
51-
TUYA_FAN_TYPE = "fs"
52-
TUYA_PET_WATER_FEEDER_TYPE = "cwysj"

custom_components/tuya_v2/cover.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
22
"""Support for Tuya Cover."""
3+
from __future__ import annotations
34

45
import logging
56
from typing import Any, List
@@ -18,7 +19,7 @@
1819
from homeassistant.helpers.dispatcher import async_dispatcher_connect
1920

2021
from .base import TuyaHaDevice
21-
from .const import DOMAIN, TUYA_DEVICE_MANAGER, TUYA_DISCOVERY_NEW, TUYA_HA_TUYA_MAP
22+
from .const import DOMAIN, TUYA_DEVICE_MANAGER, TUYA_DISCOVERY_NEW, TUYA_HA_TUYA_MAP, TUYA_HA_DEVICES
2223

2324
_LOGGER = logging.getLogger(__name__)
2425

@@ -28,6 +29,7 @@
2829
# https://developer.tuya.com/en/docs/iot/f?id=K9gf46o5mtfyc
2930
DPCODE_CONTROL = "control"
3031
DPCODE_PERCENT_CONTROL = "percent_control"
32+
DPCODE_PERCENT_STATE = "percent_state"
3133

3234
ATTR_POSITION = "position"
3335

@@ -46,6 +48,7 @@ async def async_discover_device(dev_ids):
4648
if not dev_ids:
4749
return
4850
entities = await hass.async_add_executor_job(_setup_entities, hass, dev_ids)
51+
hass.data[DOMAIN][TUYA_HA_DEVICES].extend(entities)
4952
async_add_entities(entities)
5053

5154
async_dispatcher_connect(
@@ -81,10 +84,15 @@ def device_class(self) -> str:
8184
"""Return Entity Properties."""
8285
return DEVICE_CLASS_CURTAIN
8386

87+
@property
88+
def is_closed(self) -> bool | None:
89+
return False
90+
8491
@property
8592
def current_cover_position(self) -> int:
8693
"""Return cover current position."""
87-
return self.tuya_device.status.get(DPCODE_PERCENT_CONTROL, 0)
94+
position = self.tuya_device.status.get(DPCODE_PERCENT_STATE, 0)
95+
return 100 - position
8896

8997
def open_cover(self, **kwargs: Any) -> None:
9098
"""Open the cover."""

custom_components/tuya_v2/fan.py

+41-32
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@
2929
TUYA_DEVICE_MANAGER,
3030
TUYA_DISCOVERY_NEW,
3131
TUYA_HA_DEVICES,
32-
TUYA_HA_TUYA_MAP,
33-
TUYA_AIR_PURIFIER_TYPE,
34-
TUYA_FAN_TYPE,
32+
TUYA_HA_TUYA_MAP
3533
)
3634

3735
_LOGGER = logging.getLogger(__name__)
@@ -47,21 +45,23 @@
4745

4846
# Air Purifier
4947
# https://developer.tuya.com/en/docs/iot/s?id=K9gf48r41mn81
50-
DPCODE_AP_FAN_SPEED = "speed"
48+
DPCODE_AP_FAN_SPEED = "speed"
5149
DPCODE_AP_FAN_SPEED_ENUM = "fan_speed_enum"
5250

5351
TUYA_SUPPORT_TYPE = {
54-
TUYA_FAN_TYPE,
55-
TUYA_AIR_PURIFIER_TYPE
52+
"fs", # Fan
53+
"kj", # Air Purifier
5654
}
5755

56+
5857
async def async_setup_entry(
5958
hass: HomeAssistant, _entry: ConfigEntry, async_add_entities
6059
):
6160
"""Set up tuya fan dynamically through tuya discovery."""
6261
_LOGGER.info("fan init")
6362

64-
hass.data[DOMAIN][TUYA_HA_TUYA_MAP].update({DEVICE_DOMAIN: TUYA_SUPPORT_TYPE})
63+
hass.data[DOMAIN][TUYA_HA_TUYA_MAP].update(
64+
{DEVICE_DOMAIN: TUYA_SUPPORT_TYPE})
6565

6666
async def async_discover_device(dev_ids):
6767
"""Discover and add a discovered tuya fan."""
@@ -98,30 +98,33 @@ def _setup_entities(hass, device_ids: list):
9898

9999
class TuyaHaFan(TuyaHaDevice, FanEntity):
100100
"""Tuya Fan Device."""
101-
101+
102102
def __init__(self, device: TuyaDevice, device_manager: TuyaDeviceManager):
103103
"""Init Tuya Fan Device."""
104104
super().__init__(device, device_manager)
105-
105+
106106
# Air purifier fan can be controlled either via the ranged values or via the enum.
107107
# We will always prefer the enumeration if available
108108
# Enum is used for e.g. MEES SmartHIMOX-H06
109109
# Range is used for e.g. Concept CA3000
110-
self.air_purifier_speed_range = (0, 0)
111-
self.air_purifier_speed_range_len = 0
110+
self.air_purifier_speed_range = (0, 0)
111+
self.air_purifier_speed_range_len = 0
112112
self.air_purifier_speed_range_enum = []
113-
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
113+
if self.tuya_device.category == "kj":
114114
try:
115115
if DPCODE_AP_FAN_SPEED_ENUM in self.tuya_device.status:
116-
data = json.loads(self.tuya_device.function.get(DPCODE_AP_FAN_SPEED_ENUM, {}).values).get("range")
116+
data = json.loads(self.tuya_device.function.get(
117+
DPCODE_AP_FAN_SPEED_ENUM, {}).values).get("range")
117118
if data:
118119
self.air_purifier_speed_range = (1, len(data))
119120
self.air_purifier_speed_range_len = len(data)
120121
self.air_purifier_speed_range_enum = data
121122
elif DPCODE_AP_FAN_SPEED in self.tuya_device.status:
122-
data = json.loads(self.tuya_device.function.get(DPCODE_AP_FAN_SPEED, {}).values).get("range")
123+
data = json.loads(self.tuya_device.function.get(
124+
DPCODE_AP_FAN_SPEED, {}).values).get("range")
123125
if data:
124-
self.air_purifier_speed_range = (int(data[0]), int(data[-1]))
126+
self.air_purifier_speed_range = (
127+
int(data[0]), int(data[-1]))
125128
self.air_purifier_speed_range_len = len(data)
126129
except:
127130
_LOGGER.warn("Cannot parse the air-purifier speed range")
@@ -132,17 +135,21 @@ def set_preset_mode(self, preset_mode: str) -> None:
132135

133136
def set_direction(self, direction: str) -> None:
134137
"""Set the direction of the fan."""
135-
self._send_command([{"code": DPCODE_FAN_DIRECTION, "value": direction}])
136-
138+
self._send_command(
139+
[{"code": DPCODE_FAN_DIRECTION, "value": direction}])
140+
137141
def set_percentage(self, percentage: int) -> None:
138142
"""Set the speed of the fan, as a percentage."""
139-
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
140-
value_in_range = ceil(percentage_to_ranged_value(self.air_purifier_speed_range, percentage))
143+
if self.tuya_device.category == "kj":
144+
value_in_range = ceil(percentage_to_ranged_value(
145+
self.air_purifier_speed_range, percentage))
141146
if len(self.air_purifier_speed_range_enum):
142147
# if air-purifier speed enumeration is supported we will prefer it.
143-
self._send_command([{"code": DPCODE_AP_FAN_SPEED_ENUM, "value": str(self.air_purifier_speed_range_enum[value_in_range - 1])}])
148+
self._send_command([{"code": DPCODE_AP_FAN_SPEED_ENUM, "value": str(
149+
self.air_purifier_speed_range_enum[value_in_range - 1])}])
144150
else:
145-
self._send_command([{"code": DPCODE_AP_FAN_SPEED, "value": str(value_in_range)}])
151+
self._send_command(
152+
[{"code": DPCODE_AP_FAN_SPEED, "value": str(value_in_range)}])
146153
else:
147154
super().set_percentage(percentage)
148155

@@ -162,7 +169,8 @@ def turn_on(
162169

163170
def oscillate(self, oscillating: bool) -> None:
164171
"""Oscillate the fan."""
165-
self._send_command([{"code": DPCODE_SWITCH_HORIZONTAL, "value": oscillating}])
172+
self._send_command(
173+
[{"code": DPCODE_SWITCH_HORIZONTAL, "value": oscillating}])
166174

167175
# property
168176
@property
@@ -206,29 +214,30 @@ def percentage(self) -> int:
206214
"""Return the current speed."""
207215
if not self.is_on:
208216
return 0
209-
210-
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
217+
218+
if self.tuya_device.category == "kj":
211219
if self.air_purifier_speed_range_len > 1:
212220
if len(self.air_purifier_speed_range_enum):
213221
# if air-purifier speed enumeration is supported we will prefer it.
214222
index = self.air_purifier_speed_range_enum.index(
215-
self.tuya_device.status.get(DPCODE_AP_FAN_SPEED_ENUM, 0)
223+
self.tuya_device.status.get(
224+
DPCODE_AP_FAN_SPEED_ENUM, 0)
216225
)
217226
return ranged_value_to_percentage(self.air_purifier_speed_range,
218-
index + 1
219-
)
227+
index + 1
228+
)
220229
else:
221230
return ranged_value_to_percentage(self.air_purifier_speed_range,
222-
int(self.tuya_device.status.get(DPCODE_AP_FAN_SPEED, 0)
223-
)
224-
)
231+
int(self.tuya_device.status.get(DPCODE_AP_FAN_SPEED, 0)
232+
)
233+
)
225234
else:
226235
return self.tuya_device.status.get(DPCODE_FAN_SPEED, 0)
227236

228237
@property
229238
def speed_count(self) -> int:
230239
"""Return the number of speeds the fan supports."""
231-
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
240+
if self.tuya_device.category == "kj":
232241
return self.air_purifier_speed_range_len
233242
return super().speed_count()
234243

@@ -244,7 +253,7 @@ def supported_features(self):
244253
supports = supports | SUPPORT_OSCILLATE
245254
if DPCODE_FAN_DIRECTION in self.tuya_device.status:
246255
supports = supports | SUPPORT_DIRECTION
247-
256+
248257
# Air Purifier specific
249258
if DPCODE_AP_FAN_SPEED in self.tuya_device.status or DPCODE_AP_FAN_SPEED_ENUM in self.tuya_device.status:
250259
supports = supports | SUPPORT_SET_SPEED

custom_components/tuya_v2/humidifier.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def turn_on(self, **kwargs):
135135
def turn_off(self, **kwargs):
136136
"""Turn the device off."""
137137
self._send_command([{"code": self.dp_switch, "value": False}])
138-
138+
139139
def set_humidity(self, humidity):
140140
"""Set new target humidity."""
141141
self._send_command([{"code": DPCODE_HUMIDITY_SET, "value": humidity}])

custom_components/tuya_v2/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"domain": "tuya_v2",
33
"name": "Tuya v2",
4-
"version": "1.3.1",
4+
"version": "1.3.2",
55
"documentation": "https://github.com/tuya/tuya-home-assistant",
66
"requirements": [
7-
"tuya-iot-py-sdk==0.3.0"
7+
"tuya-iot-py-sdk==0.3.1"
88
],
99
"codeowners": [
1010
"@Tuya"

0 commit comments

Comments
 (0)