Skip to content

Commit

Permalink
[fix] pir, ywbj, sj
Browse files Browse the repository at this point in the history
[fix] login problem
[add] battery state
[add] Air Purifier, Smart RGB Plug
[update] tuya-iot-python-sdk to 0.3.1
  • Loading branch information
tsutsuku committed Jul 16, 2021
1 parent 40d7829 commit 1cbaf31
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 92 deletions.
2 changes: 2 additions & 0 deletions custom_components/tuya_v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
TUYA_DISCOVERY_NEW,
TUYA_HA_DEVICES,
TUYA_HA_TUYA_MAP,
TUYA_HOME_MANAGER,
TUYA_MQTT_LISTENER,
TUYA_SETUP_PLATFORM,
TUYA_SUPPORT_HA_TYPE,
Expand Down Expand Up @@ -104,6 +105,7 @@ async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Get device list
home_manager = TuyaHomeManager(api, tuya_mq, device_manager)
await hass.async_add_executor_job(home_manager.update_device_cache)
hass.data[DOMAIN][TUYA_HOME_MANAGER] = home_manager

class DeviceListener(TuyaDeviceListener):
"""Device Update Listener."""
Expand Down
38 changes: 35 additions & 3 deletions custom_components/tuya_v2/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_BATTERY,
DOMAIN as DEVICE_DOMAIN,
BinarySensorEntity,
)
Expand Down Expand Up @@ -48,14 +49,18 @@

DPCODE_SWITCH = "switch"


DPCODE_BATTERY_STATE = "battery_state"

DPCODE_DOORCONTACT_STATE = "doorcontact_state"
DPCODE_SMOKE_SENSOR_STATE = "smoke_sensor_state"
DPCODE_SMOKE_SENSOR_STATUS = "smoke_sensor_status"
DPCODE_GAS_SENSOR_STATE = "gas_sensor_state"
DPCODE_PIR = "pir"
DPCODE_WATER_SENSOR_STATE = "watersensor_state"
DPCODE_SOS_STATE = "sos_state"
DPCODE_PRESENCE_STATE = "presence_state"

DPCODE_TEMPER_ALRAM = "temper_alarm"
DPCODE_DOORLOCK_STATE = "closed_opened"


Expand Down Expand Up @@ -133,6 +138,33 @@ def _setup_entities(hass, device_ids: List):
(lambda d: d.status.get(DPCODE_SMOKE_SENSOR_STATE, 1) == "1"),
)
)
if DPCODE_SMOKE_SENSOR_STATUS in device.status:
entities.append(
TuyaHaBSensor(
device,
device_manager,
DEVICE_CLASS_SMOKE,
(lambda d: d.status.get(DPCODE_SMOKE_SENSOR_STATUS, 'normal') == "alarm"),
)
)
if DPCODE_BATTERY_STATE in device.status:
entities.append(
TuyaHaBSensor(
device,
device_manager,
DEVICE_CLASS_BATTERY,
(lambda d: d.status.get(DPCODE_BATTERY_STATE, 'normal') == "low"),
)
)
if DPCODE_TEMPER_ALRAM in device.status:

This comment has been minimized.

Copy link
@jpros

jpros Jul 21, 2021

Typo in "ALARM"

entities.append(
TuyaHaBSensor(
device,
device_manager,
DEVICE_CLASS_MOTION,
(lambda d: d.status.get(DPCODE_TEMPER_ALRAM, False)),

This comment has been minimized.

Copy link
@jpros

jpros Jul 21, 2021

Typo in "ALARM"

)
)
if DPCODE_GAS_SENSOR_STATE in device.status:
entities.append(
TuyaHaBSensor(
Expand All @@ -148,7 +180,7 @@ def _setup_entities(hass, device_ids: List):
device,
device_manager,
DEVICE_CLASS_MOTION,
(lambda d: d.status.get(DPCODE_PIR, "none") == "1"),
(lambda d: d.status.get(DPCODE_PIR, "none") == "pir"),
)
)
if DPCODE_WATER_SENSOR_STATE in device.status:
Expand All @@ -157,7 +189,7 @@ def _setup_entities(hass, device_ids: List):
device,
device_manager,
DEVICE_CLASS_MOISTURE,
(lambda d: d.status.get(DPCODE_WATER_SENSOR_STATE, "none") == "1"),
(lambda d: d.status.get(DPCODE_WATER_SENSOR_STATE, "normal") == "alarm"),
)
)
if DPCODE_SOS_STATE in device.status:
Expand Down
17 changes: 8 additions & 9 deletions custom_components/tuya_v2/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,14 @@ def _try_login(cls, user_input):
if project_type == ProjectType.INDUSTY_SOLUTIONS:
response = api.login(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
else:
for endpoint in TUYA_ENDPOINT.keys():
api.endpoint = endpoint
response = api.login(user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_COUNTRY_CODE],
user_input[CONF_APP_TYPE])
if response.get("success", False):
user_input[CONF_ENDPOINT] = endpoint
break
api.endpoint = "https://openapi.tuyacn.com"
response = api.login(user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_COUNTRY_CODE],
user_input[CONF_APP_TYPE])
if response.get("success", False):
api.endpoint = api.token_info.platform_url
user_input[CONF_ENDPOINT] = api.token_info.platform_url

_LOGGER.info(f"TuyaConfigFlow._try_login finish, response:, {response}")
return response
Expand Down
8 changes: 3 additions & 5 deletions custom_components/tuya_v2/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

TUYA_DISCOVERY_NEW = "tuya_v2_discovery_new_{}"
TUYA_DEVICE_MANAGER = "tuya_device_manager"
TUYA_HOME_MANAGER = "tuya_home_manager"
TUYA_MQTT_LISTENER = "tuya_mqtt_listener"
TUYA_HA_TUYA_MAP = "tuya_ha_tuya_map"
TUYA_HA_DEVICES = "tuya_ha_devices"
Expand Down Expand Up @@ -43,10 +44,7 @@
"humidifier",
"number",
"vacuum",
"select"
"select",
# "remote"
# 'alarm_control_panel'
]

TUYA_AIR_PURIFIER_TYPE = "kj"
TUYA_FAN_TYPE = "fs"
TUYA_PET_WATER_FEEDER_TYPE = "cwysj"
12 changes: 10 additions & 2 deletions custom_components/tuya_v2/cover.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Support for Tuya Cover."""
from __future__ import annotations

import logging
from typing import Any, List
Expand All @@ -18,7 +19,7 @@
from homeassistant.helpers.dispatcher import async_dispatcher_connect

from .base import TuyaHaDevice
from .const import DOMAIN, TUYA_DEVICE_MANAGER, TUYA_DISCOVERY_NEW, TUYA_HA_TUYA_MAP
from .const import DOMAIN, TUYA_DEVICE_MANAGER, TUYA_DISCOVERY_NEW, TUYA_HA_TUYA_MAP, TUYA_HA_DEVICES

_LOGGER = logging.getLogger(__name__)

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

ATTR_POSITION = "position"

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

async_dispatcher_connect(
Expand Down Expand Up @@ -81,10 +84,15 @@ def device_class(self) -> str:
"""Return Entity Properties."""
return DEVICE_CLASS_CURTAIN

@property
def is_closed(self) -> bool | None:
return False

@property
def current_cover_position(self) -> int:
"""Return cover current position."""
return self.tuya_device.status.get(DPCODE_PERCENT_CONTROL, 0)
position = self.tuya_device.status.get(DPCODE_PERCENT_STATE, 0)
return 100 - position

def open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
Expand Down
73 changes: 41 additions & 32 deletions custom_components/tuya_v2/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
TUYA_DEVICE_MANAGER,
TUYA_DISCOVERY_NEW,
TUYA_HA_DEVICES,
TUYA_HA_TUYA_MAP,
TUYA_AIR_PURIFIER_TYPE,
TUYA_FAN_TYPE,
TUYA_HA_TUYA_MAP
)

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

# Air Purifier
# https://developer.tuya.com/en/docs/iot/s?id=K9gf48r41mn81
DPCODE_AP_FAN_SPEED = "speed"
DPCODE_AP_FAN_SPEED = "speed"
DPCODE_AP_FAN_SPEED_ENUM = "fan_speed_enum"

TUYA_SUPPORT_TYPE = {
TUYA_FAN_TYPE,
TUYA_AIR_PURIFIER_TYPE
"fs", # Fan

This comment has been minimized.

Copy link
@jpros

jpros Jul 21, 2021

Why did you replace the constant with string in the code?
Perhaps it would be easier for other maintainers to work with the constant, as the names are the abbreviation in Chinese.

"kj", # Air Purifier
}


async def async_setup_entry(
hass: HomeAssistant, _entry: ConfigEntry, async_add_entities
):
"""Set up tuya fan dynamically through tuya discovery."""
_LOGGER.info("fan init")

hass.data[DOMAIN][TUYA_HA_TUYA_MAP].update({DEVICE_DOMAIN: TUYA_SUPPORT_TYPE})
hass.data[DOMAIN][TUYA_HA_TUYA_MAP].update(
{DEVICE_DOMAIN: TUYA_SUPPORT_TYPE})

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

class TuyaHaFan(TuyaHaDevice, FanEntity):
"""Tuya Fan Device."""

def __init__(self, device: TuyaDevice, device_manager: TuyaDeviceManager):
"""Init Tuya Fan Device."""
super().__init__(device, device_manager)

# Air purifier fan can be controlled either via the ranged values or via the enum.
# We will always prefer the enumeration if available
# Enum is used for e.g. MEES SmartHIMOX-H06
# Range is used for e.g. Concept CA3000
self.air_purifier_speed_range = (0, 0)
self.air_purifier_speed_range_len = 0
self.air_purifier_speed_range = (0, 0)
self.air_purifier_speed_range_len = 0
self.air_purifier_speed_range_enum = []
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
if self.tuya_device.category == "kj":
try:
if DPCODE_AP_FAN_SPEED_ENUM in self.tuya_device.status:
data = json.loads(self.tuya_device.function.get(DPCODE_AP_FAN_SPEED_ENUM, {}).values).get("range")
data = json.loads(self.tuya_device.function.get(
DPCODE_AP_FAN_SPEED_ENUM, {}).values).get("range")
if data:
self.air_purifier_speed_range = (1, len(data))
self.air_purifier_speed_range_len = len(data)
self.air_purifier_speed_range_enum = data
elif DPCODE_AP_FAN_SPEED in self.tuya_device.status:
data = json.loads(self.tuya_device.function.get(DPCODE_AP_FAN_SPEED, {}).values).get("range")
data = json.loads(self.tuya_device.function.get(
DPCODE_AP_FAN_SPEED, {}).values).get("range")
if data:
self.air_purifier_speed_range = (int(data[0]), int(data[-1]))
self.air_purifier_speed_range = (
int(data[0]), int(data[-1]))
self.air_purifier_speed_range_len = len(data)
except:
_LOGGER.warn("Cannot parse the air-purifier speed range")
Expand All @@ -132,17 +135,21 @@ def set_preset_mode(self, preset_mode: str) -> None:

def set_direction(self, direction: str) -> None:
"""Set the direction of the fan."""
self._send_command([{"code": DPCODE_FAN_DIRECTION, "value": direction}])

self._send_command(
[{"code": DPCODE_FAN_DIRECTION, "value": direction}])

def set_percentage(self, percentage: int) -> None:
"""Set the speed of the fan, as a percentage."""
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
value_in_range = ceil(percentage_to_ranged_value(self.air_purifier_speed_range, percentage))
if self.tuya_device.category == "kj":
value_in_range = ceil(percentage_to_ranged_value(
self.air_purifier_speed_range, percentage))
if len(self.air_purifier_speed_range_enum):
# if air-purifier speed enumeration is supported we will prefer it.
self._send_command([{"code": DPCODE_AP_FAN_SPEED_ENUM, "value": str(self.air_purifier_speed_range_enum[value_in_range - 1])}])
self._send_command([{"code": DPCODE_AP_FAN_SPEED_ENUM, "value": str(
self.air_purifier_speed_range_enum[value_in_range - 1])}])
else:
self._send_command([{"code": DPCODE_AP_FAN_SPEED, "value": str(value_in_range)}])
self._send_command(
[{"code": DPCODE_AP_FAN_SPEED, "value": str(value_in_range)}])
else:
super().set_percentage(percentage)

Expand All @@ -162,7 +169,8 @@ def turn_on(

def oscillate(self, oscillating: bool) -> None:
"""Oscillate the fan."""
self._send_command([{"code": DPCODE_SWITCH_HORIZONTAL, "value": oscillating}])
self._send_command(
[{"code": DPCODE_SWITCH_HORIZONTAL, "value": oscillating}])

# property
@property
Expand Down Expand Up @@ -206,29 +214,30 @@ def percentage(self) -> int:
"""Return the current speed."""
if not self.is_on:
return 0
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:

if self.tuya_device.category == "kj":
if self.air_purifier_speed_range_len > 1:
if len(self.air_purifier_speed_range_enum):
# if air-purifier speed enumeration is supported we will prefer it.
index = self.air_purifier_speed_range_enum.index(
self.tuya_device.status.get(DPCODE_AP_FAN_SPEED_ENUM, 0)
self.tuya_device.status.get(
DPCODE_AP_FAN_SPEED_ENUM, 0)
)
return ranged_value_to_percentage(self.air_purifier_speed_range,
index + 1
)
index + 1
)
else:
return ranged_value_to_percentage(self.air_purifier_speed_range,
int(self.tuya_device.status.get(DPCODE_AP_FAN_SPEED, 0)
)
)
int(self.tuya_device.status.get(DPCODE_AP_FAN_SPEED, 0)
)
)
else:
return self.tuya_device.status.get(DPCODE_FAN_SPEED, 0)

@property
def speed_count(self) -> int:
"""Return the number of speeds the fan supports."""
if self.tuya_device.category == TUYA_AIR_PURIFIER_TYPE:
if self.tuya_device.category == "kj":
return self.air_purifier_speed_range_len
return super().speed_count()

Expand All @@ -244,7 +253,7 @@ def supported_features(self):
supports = supports | SUPPORT_OSCILLATE
if DPCODE_FAN_DIRECTION in self.tuya_device.status:
supports = supports | SUPPORT_DIRECTION

# Air Purifier specific
if DPCODE_AP_FAN_SPEED in self.tuya_device.status or DPCODE_AP_FAN_SPEED_ENUM in self.tuya_device.status:
supports = supports | SUPPORT_SET_SPEED
Expand Down
2 changes: 1 addition & 1 deletion custom_components/tuya_v2/humidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def turn_on(self, **kwargs):
def turn_off(self, **kwargs):
"""Turn the device off."""
self._send_command([{"code": self.dp_switch, "value": False}])

def set_humidity(self, humidity):
"""Set new target humidity."""
self._send_command([{"code": DPCODE_HUMIDITY_SET, "value": humidity}])
4 changes: 2 additions & 2 deletions custom_components/tuya_v2/manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"domain": "tuya_v2",
"name": "Tuya v2",
"version": "1.3.1",
"version": "1.3.2",
"documentation": "https://github.com/tuya/tuya-home-assistant",
"requirements": [
"tuya-iot-py-sdk==0.3.0"
"tuya-iot-py-sdk==0.3.1"
],
"codeowners": [
"@Tuya"
Expand Down
Loading

0 comments on commit 1cbaf31

Please sign in to comment.