Bump tuyaha to 0.0.10 and fix set temperature issues (#45732)
This commit is contained in:
parent
1bb535aa67
commit
3c26235e78
11 changed files with 318 additions and 52 deletions
|
@ -993,7 +993,14 @@ omit =
|
||||||
homeassistant/components/transmission/const.py
|
homeassistant/components/transmission/const.py
|
||||||
homeassistant/components/transmission/errors.py
|
homeassistant/components/transmission/errors.py
|
||||||
homeassistant/components/travisci/sensor.py
|
homeassistant/components/travisci/sensor.py
|
||||||
homeassistant/components/tuya/*
|
homeassistant/components/tuya/__init__.py
|
||||||
|
homeassistant/components/tuya/climate.py
|
||||||
|
homeassistant/components/tuya/const.py
|
||||||
|
homeassistant/components/tuya/cover.py
|
||||||
|
homeassistant/components/tuya/fan.py
|
||||||
|
homeassistant/components/tuya/light.py
|
||||||
|
homeassistant/components/tuya/scene.py
|
||||||
|
homeassistant/components/tuya/switch.py
|
||||||
homeassistant/components/twentemilieu/const.py
|
homeassistant/components/twentemilieu/const.py
|
||||||
homeassistant/components/twentemilieu/sensor.py
|
homeassistant/components/twentemilieu/sensor.py
|
||||||
homeassistant/components/twilio_call/notify.py
|
homeassistant/components/twilio_call/notify.py
|
||||||
|
|
|
@ -33,7 +33,9 @@ from .const import (
|
||||||
CONF_CURR_TEMP_DIVIDER,
|
CONF_CURR_TEMP_DIVIDER,
|
||||||
CONF_MAX_TEMP,
|
CONF_MAX_TEMP,
|
||||||
CONF_MIN_TEMP,
|
CONF_MIN_TEMP,
|
||||||
|
CONF_SET_TEMP_DIVIDED,
|
||||||
CONF_TEMP_DIVIDER,
|
CONF_TEMP_DIVIDER,
|
||||||
|
CONF_TEMP_STEP_OVERRIDE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SIGNAL_CONFIG_ENTITY,
|
SIGNAL_CONFIG_ENTITY,
|
||||||
TUYA_DATA,
|
TUYA_DATA,
|
||||||
|
@ -103,6 +105,8 @@ class TuyaClimateEntity(TuyaDevice, ClimateEntity):
|
||||||
self.operations = [HVAC_MODE_OFF]
|
self.operations = [HVAC_MODE_OFF]
|
||||||
self._has_operation = False
|
self._has_operation = False
|
||||||
self._def_hvac_mode = HVAC_MODE_AUTO
|
self._def_hvac_mode = HVAC_MODE_AUTO
|
||||||
|
self._set_temp_divided = True
|
||||||
|
self._temp_step_override = None
|
||||||
self._min_temp = None
|
self._min_temp = None
|
||||||
self._max_temp = None
|
self._max_temp = None
|
||||||
|
|
||||||
|
@ -117,6 +121,8 @@ class TuyaClimateEntity(TuyaDevice, ClimateEntity):
|
||||||
self._tuya.set_unit("FAHRENHEIT" if unit == TEMP_FAHRENHEIT else "CELSIUS")
|
self._tuya.set_unit("FAHRENHEIT" if unit == TEMP_FAHRENHEIT else "CELSIUS")
|
||||||
self._tuya.temp_divider = config.get(CONF_TEMP_DIVIDER, 0)
|
self._tuya.temp_divider = config.get(CONF_TEMP_DIVIDER, 0)
|
||||||
self._tuya.curr_temp_divider = config.get(CONF_CURR_TEMP_DIVIDER, 0)
|
self._tuya.curr_temp_divider = config.get(CONF_CURR_TEMP_DIVIDER, 0)
|
||||||
|
self._set_temp_divided = config.get(CONF_SET_TEMP_DIVIDED, True)
|
||||||
|
self._temp_step_override = config.get(CONF_TEMP_STEP_OVERRIDE)
|
||||||
min_temp = config.get(CONF_MIN_TEMP, 0)
|
min_temp = config.get(CONF_MIN_TEMP, 0)
|
||||||
max_temp = config.get(CONF_MAX_TEMP, 0)
|
max_temp = config.get(CONF_MAX_TEMP, 0)
|
||||||
if min_temp >= max_temp:
|
if min_temp >= max_temp:
|
||||||
|
@ -189,6 +195,8 @@ class TuyaClimateEntity(TuyaDevice, ClimateEntity):
|
||||||
@property
|
@property
|
||||||
def target_temperature_step(self):
|
def target_temperature_step(self):
|
||||||
"""Return the supported step of target temperature."""
|
"""Return the supported step of target temperature."""
|
||||||
|
if self._temp_step_override:
|
||||||
|
return self._temp_step_override
|
||||||
return self._tuya.target_temperature_step()
|
return self._tuya.target_temperature_step()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -204,7 +212,7 @@ class TuyaClimateEntity(TuyaDevice, ClimateEntity):
|
||||||
def set_temperature(self, **kwargs):
|
def set_temperature(self, **kwargs):
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
if ATTR_TEMPERATURE in kwargs:
|
if ATTR_TEMPERATURE in kwargs:
|
||||||
self._tuya.set_temperature(kwargs[ATTR_TEMPERATURE])
|
self._tuya.set_temperature(kwargs[ATTR_TEMPERATURE], self._set_temp_divided)
|
||||||
|
|
||||||
def set_fan_mode(self, fan_mode):
|
def set_fan_mode(self, fan_mode):
|
||||||
"""Set new target fan mode."""
|
"""Set new target fan mode."""
|
||||||
|
|
|
@ -23,7 +23,6 @@ from homeassistant.const import (
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
# pylint:disable=unused-import
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_BRIGHTNESS_RANGE_MODE,
|
CONF_BRIGHTNESS_RANGE_MODE,
|
||||||
CONF_COUNTRYCODE,
|
CONF_COUNTRYCODE,
|
||||||
|
@ -35,17 +34,19 @@ from .const import (
|
||||||
CONF_MIN_TEMP,
|
CONF_MIN_TEMP,
|
||||||
CONF_QUERY_DEVICE,
|
CONF_QUERY_DEVICE,
|
||||||
CONF_QUERY_INTERVAL,
|
CONF_QUERY_INTERVAL,
|
||||||
|
CONF_SET_TEMP_DIVIDED,
|
||||||
CONF_SUPPORT_COLOR,
|
CONF_SUPPORT_COLOR,
|
||||||
CONF_TEMP_DIVIDER,
|
CONF_TEMP_DIVIDER,
|
||||||
|
CONF_TEMP_STEP_OVERRIDE,
|
||||||
CONF_TUYA_MAX_COLTEMP,
|
CONF_TUYA_MAX_COLTEMP,
|
||||||
DEFAULT_DISCOVERY_INTERVAL,
|
DEFAULT_DISCOVERY_INTERVAL,
|
||||||
DEFAULT_QUERY_INTERVAL,
|
DEFAULT_QUERY_INTERVAL,
|
||||||
DEFAULT_TUYA_MAX_COLTEMP,
|
DEFAULT_TUYA_MAX_COLTEMP,
|
||||||
DOMAIN,
|
|
||||||
TUYA_DATA,
|
TUYA_DATA,
|
||||||
TUYA_PLATFORMS,
|
TUYA_PLATFORMS,
|
||||||
TUYA_TYPE_NOT_QUERY,
|
TUYA_TYPE_NOT_QUERY,
|
||||||
)
|
)
|
||||||
|
from .const import DOMAIN # pylint:disable=unused-import
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ ERROR_DEV_NOT_FOUND = "dev_not_found"
|
||||||
|
|
||||||
RESULT_AUTH_FAILED = "invalid_auth"
|
RESULT_AUTH_FAILED = "invalid_auth"
|
||||||
RESULT_CONN_ERROR = "cannot_connect"
|
RESULT_CONN_ERROR = "cannot_connect"
|
||||||
|
RESULT_SINGLE_INSTANCE = "single_instance_allowed"
|
||||||
RESULT_SUCCESS = "success"
|
RESULT_SUCCESS = "success"
|
||||||
|
|
||||||
RESULT_LOG_MESSAGE = {
|
RESULT_LOG_MESSAGE = {
|
||||||
|
@ -123,7 +125,7 @@ class TuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Handle a flow initialized by the user."""
|
"""Handle a flow initialized by the user."""
|
||||||
if self._async_current_entries():
|
if self._async_current_entries():
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
return self.async_abort(reason=RESULT_SINGLE_INSTANCE)
|
||||||
|
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
|
@ -257,7 +259,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
|
|
||||||
if self.config_entry.state != config_entries.ENTRY_STATE_LOADED:
|
if self.config_entry.state != config_entries.ENTRY_STATE_LOADED:
|
||||||
_LOGGER.error("Tuya integration not yet loaded")
|
_LOGGER.error("Tuya integration not yet loaded")
|
||||||
return self.async_abort(reason="cannot_connect")
|
return self.async_abort(reason=RESULT_CONN_ERROR)
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
dev_ids = user_input.get(CONF_LIST_DEVICES)
|
dev_ids = user_input.get(CONF_LIST_DEVICES)
|
||||||
|
@ -323,11 +325,14 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
|
|
||||||
def _get_device_schema(self, device_type, curr_conf, device):
|
def _get_device_schema(self, device_type, curr_conf, device):
|
||||||
"""Return option schema for device."""
|
"""Return option schema for device."""
|
||||||
|
if device_type != device.device_type():
|
||||||
|
return None
|
||||||
|
schema = None
|
||||||
if device_type == "light":
|
if device_type == "light":
|
||||||
return self._get_light_schema(curr_conf, device)
|
schema = self._get_light_schema(curr_conf, device)
|
||||||
if device_type == "climate":
|
elif device_type == "climate":
|
||||||
return self._get_climate_schema(curr_conf, device)
|
schema = self._get_climate_schema(curr_conf, device)
|
||||||
return None
|
return schema
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_light_schema(curr_conf, device):
|
def _get_light_schema(curr_conf, device):
|
||||||
|
@ -374,6 +379,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
"""Create option schema for climate device."""
|
"""Create option schema for climate device."""
|
||||||
unit = device.temperature_unit()
|
unit = device.temperature_unit()
|
||||||
def_unit = TEMP_FAHRENHEIT if unit == "FAHRENHEIT" else TEMP_CELSIUS
|
def_unit = TEMP_FAHRENHEIT if unit == "FAHRENHEIT" else TEMP_CELSIUS
|
||||||
|
supported_steps = device.supported_temperature_steps()
|
||||||
|
default_step = device.target_temperature_step()
|
||||||
|
|
||||||
config_schema = vol.Schema(
|
config_schema = vol.Schema(
|
||||||
{
|
{
|
||||||
|
@ -389,6 +396,14 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
CONF_CURR_TEMP_DIVIDER,
|
CONF_CURR_TEMP_DIVIDER,
|
||||||
default=curr_conf.get(CONF_CURR_TEMP_DIVIDER, 0),
|
default=curr_conf.get(CONF_CURR_TEMP_DIVIDER, 0),
|
||||||
): vol.All(vol.Coerce(int), vol.Clamp(min=0)),
|
): vol.All(vol.Coerce(int), vol.Clamp(min=0)),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_SET_TEMP_DIVIDED,
|
||||||
|
default=curr_conf.get(CONF_SET_TEMP_DIVIDED, True),
|
||||||
|
): bool,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_TEMP_STEP_OVERRIDE,
|
||||||
|
default=curr_conf.get(CONF_TEMP_STEP_OVERRIDE, default_step),
|
||||||
|
): vol.In(supported_steps),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_MIN_TEMP,
|
CONF_MIN_TEMP,
|
||||||
default=curr_conf.get(CONF_MIN_TEMP, 0),
|
default=curr_conf.get(CONF_MIN_TEMP, 0),
|
||||||
|
|
|
@ -10,8 +10,10 @@ CONF_MIN_KELVIN = "min_kelvin"
|
||||||
CONF_MIN_TEMP = "min_temp"
|
CONF_MIN_TEMP = "min_temp"
|
||||||
CONF_QUERY_DEVICE = "query_device"
|
CONF_QUERY_DEVICE = "query_device"
|
||||||
CONF_QUERY_INTERVAL = "query_interval"
|
CONF_QUERY_INTERVAL = "query_interval"
|
||||||
|
CONF_SET_TEMP_DIVIDED = "set_temp_divided"
|
||||||
CONF_SUPPORT_COLOR = "support_color"
|
CONF_SUPPORT_COLOR = "support_color"
|
||||||
CONF_TEMP_DIVIDER = "temp_divider"
|
CONF_TEMP_DIVIDER = "temp_divider"
|
||||||
|
CONF_TEMP_STEP_OVERRIDE = "temp_step_override"
|
||||||
CONF_TUYA_MAX_COLTEMP = "tuya_max_coltemp"
|
CONF_TUYA_MAX_COLTEMP = "tuya_max_coltemp"
|
||||||
|
|
||||||
DEFAULT_DISCOVERY_INTERVAL = 605
|
DEFAULT_DISCOVERY_INTERVAL = 605
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "tuya",
|
"domain": "tuya",
|
||||||
"name": "Tuya",
|
"name": "Tuya",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/tuya",
|
"documentation": "https://www.home-assistant.io/integrations/tuya",
|
||||||
"requirements": ["tuyaha==0.0.9"],
|
"requirements": ["tuyaha==0.0.10"],
|
||||||
"codeowners": ["@ollo69"],
|
"codeowners": ["@ollo69"],
|
||||||
"config_flow": true
|
"config_flow": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
"unit_of_measurement": "Temperature unit used by device",
|
"unit_of_measurement": "Temperature unit used by device",
|
||||||
"temp_divider": "Temperature values divider (0 = use default)",
|
"temp_divider": "Temperature values divider (0 = use default)",
|
||||||
"curr_temp_divider": "Current Temperature value divider (0 = use default)",
|
"curr_temp_divider": "Current Temperature value divider (0 = use default)",
|
||||||
|
"set_temp_divided": "Use divided Temperature value for set temperature command",
|
||||||
|
"temp_step_override": "Target Temperature step",
|
||||||
"min_temp": "Min target temperature (use min and max = 0 for default)",
|
"min_temp": "Min target temperature (use min and max = 0 for default)",
|
||||||
"max_temp": "Max target temperature (use min and max = 0 for default)"
|
"max_temp": "Max target temperature (use min and max = 0 for default)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,10 @@
|
||||||
"max_temp": "Max target temperature (use min and max = 0 for default)",
|
"max_temp": "Max target temperature (use min and max = 0 for default)",
|
||||||
"min_kelvin": "Min color temperature supported in kelvin",
|
"min_kelvin": "Min color temperature supported in kelvin",
|
||||||
"min_temp": "Min target temperature (use min and max = 0 for default)",
|
"min_temp": "Min target temperature (use min and max = 0 for default)",
|
||||||
|
"set_temp_divided": "Use divided Temperature value for set temperature command",
|
||||||
"support_color": "Force color support",
|
"support_color": "Force color support",
|
||||||
"temp_divider": "Temperature values divider (0 = use default)",
|
"temp_divider": "Temperature values divider (0 = use default)",
|
||||||
|
"temp_step_override": "Target Temperature step",
|
||||||
"tuya_max_coltemp": "Max color temperature reported by device",
|
"tuya_max_coltemp": "Max color temperature reported by device",
|
||||||
"unit_of_measurement": "Temperature unit used by device"
|
"unit_of_measurement": "Temperature unit used by device"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2217,7 +2217,7 @@ tp-connected==0.0.4
|
||||||
transmissionrpc==0.11
|
transmissionrpc==0.11
|
||||||
|
|
||||||
# homeassistant.components.tuya
|
# homeassistant.components.tuya
|
||||||
tuyaha==0.0.9
|
tuyaha==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.twentemilieu
|
# homeassistant.components.twentemilieu
|
||||||
twentemilieu==0.3.0
|
twentemilieu==0.3.0
|
||||||
|
|
|
@ -1126,7 +1126,7 @@ total_connect_client==0.55
|
||||||
transmissionrpc==0.11
|
transmissionrpc==0.11
|
||||||
|
|
||||||
# homeassistant.components.tuya
|
# homeassistant.components.tuya
|
||||||
tuyaha==0.0.9
|
tuyaha==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.twentemilieu
|
# homeassistant.components.twentemilieu
|
||||||
twentemilieu==0.3.0
|
twentemilieu==0.3.0
|
||||||
|
|
75
tests/components/tuya/common.py
Normal file
75
tests/components/tuya/common.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
"""Test code shared between test files."""
|
||||||
|
|
||||||
|
from tuyaha.devices import climate, light, switch
|
||||||
|
|
||||||
|
CLIMATE_ID = "1"
|
||||||
|
CLIMATE_DATA = {
|
||||||
|
"data": {"state": "true", "temp_unit": climate.UNIT_CELSIUS},
|
||||||
|
"id": CLIMATE_ID,
|
||||||
|
"ha_type": "climate",
|
||||||
|
"name": "TestClimate",
|
||||||
|
"dev_type": "climate",
|
||||||
|
}
|
||||||
|
|
||||||
|
LIGHT_ID = "2"
|
||||||
|
LIGHT_DATA = {
|
||||||
|
"data": {"state": "true"},
|
||||||
|
"id": LIGHT_ID,
|
||||||
|
"ha_type": "light",
|
||||||
|
"name": "TestLight",
|
||||||
|
"dev_type": "light",
|
||||||
|
}
|
||||||
|
|
||||||
|
SWITCH_ID = "3"
|
||||||
|
SWITCH_DATA = {
|
||||||
|
"data": {"state": True},
|
||||||
|
"id": SWITCH_ID,
|
||||||
|
"ha_type": "switch",
|
||||||
|
"name": "TestSwitch",
|
||||||
|
"dev_type": "switch",
|
||||||
|
}
|
||||||
|
|
||||||
|
LIGHT_ID_FAKE1 = "9998"
|
||||||
|
LIGHT_DATA_FAKE1 = {
|
||||||
|
"data": {"state": "true"},
|
||||||
|
"id": LIGHT_ID_FAKE1,
|
||||||
|
"ha_type": "light",
|
||||||
|
"name": "TestLightFake1",
|
||||||
|
"dev_type": "light",
|
||||||
|
}
|
||||||
|
|
||||||
|
LIGHT_ID_FAKE2 = "9999"
|
||||||
|
LIGHT_DATA_FAKE2 = {
|
||||||
|
"data": {"state": "true"},
|
||||||
|
"id": LIGHT_ID_FAKE2,
|
||||||
|
"ha_type": "light",
|
||||||
|
"name": "TestLightFake2",
|
||||||
|
"dev_type": "light",
|
||||||
|
}
|
||||||
|
|
||||||
|
TUYA_DEVICES = [
|
||||||
|
climate.TuyaClimate(CLIMATE_DATA, None),
|
||||||
|
light.TuyaLight(LIGHT_DATA, None),
|
||||||
|
switch.TuyaSwitch(SWITCH_DATA, None),
|
||||||
|
light.TuyaLight(LIGHT_DATA_FAKE1, None),
|
||||||
|
light.TuyaLight(LIGHT_DATA_FAKE2, None),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MockTuya:
|
||||||
|
"""Mock for Tuya devices."""
|
||||||
|
|
||||||
|
def get_all_devices(self):
|
||||||
|
"""Return all configured devices."""
|
||||||
|
return TUYA_DEVICES
|
||||||
|
|
||||||
|
def get_device_by_id(self, dev_id):
|
||||||
|
"""Return configured device with dev id."""
|
||||||
|
if dev_id == LIGHT_ID_FAKE1:
|
||||||
|
return None
|
||||||
|
if dev_id == LIGHT_ID_FAKE2:
|
||||||
|
return switch.TuyaSwitch(SWITCH_DATA, None)
|
||||||
|
for device in TUYA_DEVICES:
|
||||||
|
if device.object_id() == dev_id:
|
||||||
|
return device
|
||||||
|
return None
|
|
@ -2,11 +2,47 @@
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from tuyaha.devices.climate import STEP_HALVES
|
||||||
from tuyaha.tuyaapi import TuyaAPIException, TuyaNetException
|
from tuyaha.tuyaapi import TuyaAPIException, TuyaNetException
|
||||||
|
|
||||||
from homeassistant import config_entries, data_entry_flow, setup
|
from homeassistant import config_entries, data_entry_flow
|
||||||
from homeassistant.components.tuya.const import CONF_COUNTRYCODE, DOMAIN
|
from homeassistant.components.tuya.config_flow import (
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_PLATFORM, CONF_USERNAME
|
CONF_LIST_DEVICES,
|
||||||
|
ERROR_DEV_MULTI_TYPE,
|
||||||
|
ERROR_DEV_NOT_CONFIG,
|
||||||
|
ERROR_DEV_NOT_FOUND,
|
||||||
|
RESULT_AUTH_FAILED,
|
||||||
|
RESULT_CONN_ERROR,
|
||||||
|
RESULT_SINGLE_INSTANCE,
|
||||||
|
)
|
||||||
|
from homeassistant.components.tuya.const import (
|
||||||
|
CONF_BRIGHTNESS_RANGE_MODE,
|
||||||
|
CONF_COUNTRYCODE,
|
||||||
|
CONF_CURR_TEMP_DIVIDER,
|
||||||
|
CONF_DISCOVERY_INTERVAL,
|
||||||
|
CONF_MAX_KELVIN,
|
||||||
|
CONF_MAX_TEMP,
|
||||||
|
CONF_MIN_KELVIN,
|
||||||
|
CONF_MIN_TEMP,
|
||||||
|
CONF_QUERY_DEVICE,
|
||||||
|
CONF_QUERY_INTERVAL,
|
||||||
|
CONF_SET_TEMP_DIVIDED,
|
||||||
|
CONF_SUPPORT_COLOR,
|
||||||
|
CONF_TEMP_DIVIDER,
|
||||||
|
CONF_TEMP_STEP_OVERRIDE,
|
||||||
|
CONF_TUYA_MAX_COLTEMP,
|
||||||
|
DOMAIN,
|
||||||
|
TUYA_DATA,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_PLATFORM,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
|
CONF_USERNAME,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .common import CLIMATE_ID, LIGHT_ID, LIGHT_ID_FAKE1, LIGHT_ID_FAKE2, MockTuya
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
@ -30,9 +66,15 @@ def tuya_fixture() -> Mock:
|
||||||
yield tuya
|
yield tuya
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="tuya_setup", autouse=True)
|
||||||
|
def tuya_setup_fixture():
|
||||||
|
"""Mock tuya entry setup."""
|
||||||
|
with patch("homeassistant.components.tuya.async_setup_entry", return_value=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
async def test_user(hass, tuya):
|
async def test_user(hass, tuya):
|
||||||
"""Test user config."""
|
"""Test user config."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
@ -40,15 +82,10 @@ async def test_user(hass, tuya):
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
with patch(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
"homeassistant.components.tuya.async_setup", return_value=True
|
result["flow_id"], user_input=TUYA_USER_DATA
|
||||||
) as mock_setup, patch(
|
)
|
||||||
"homeassistant.components.tuya.async_setup_entry", return_value=True
|
await hass.async_block_till_done()
|
||||||
) as mock_setup_entry:
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"], user_input=TUYA_USER_DATA
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
assert result["title"] == USERNAME
|
assert result["title"] == USERNAME
|
||||||
|
@ -58,26 +95,15 @@ async def test_user(hass, tuya):
|
||||||
assert result["data"][CONF_PLATFORM] == TUYA_PLATFORM
|
assert result["data"][CONF_PLATFORM] == TUYA_PLATFORM
|
||||||
assert not result["result"].unique_id
|
assert not result["result"].unique_id
|
||||||
|
|
||||||
assert len(mock_setup.mock_calls) == 1
|
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
|
||||||
|
|
||||||
|
|
||||||
async def test_import(hass, tuya):
|
async def test_import(hass, tuya):
|
||||||
"""Test import step."""
|
"""Test import step."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
result = await hass.config_entries.flow.async_init(
|
||||||
with patch(
|
DOMAIN,
|
||||||
"homeassistant.components.tuya.async_setup",
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
return_value=True,
|
data=TUYA_USER_DATA,
|
||||||
) as mock_setup, patch(
|
)
|
||||||
"homeassistant.components.tuya.async_setup_entry",
|
await hass.async_block_till_done()
|
||||||
return_value=True,
|
|
||||||
) as mock_setup_entry:
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN,
|
|
||||||
context={"source": config_entries.SOURCE_IMPORT},
|
|
||||||
data=TUYA_USER_DATA,
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
assert result["title"] == USERNAME
|
assert result["title"] == USERNAME
|
||||||
|
@ -87,9 +113,6 @@ async def test_import(hass, tuya):
|
||||||
assert result["data"][CONF_PLATFORM] == TUYA_PLATFORM
|
assert result["data"][CONF_PLATFORM] == TUYA_PLATFORM
|
||||||
assert not result["result"].unique_id
|
assert not result["result"].unique_id
|
||||||
|
|
||||||
assert len(mock_setup.mock_calls) == 1
|
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_if_already_setup(hass, tuya):
|
async def test_abort_if_already_setup(hass, tuya):
|
||||||
"""Test we abort if Tuya is already setup."""
|
"""Test we abort if Tuya is already setup."""
|
||||||
|
@ -101,7 +124,7 @@ async def test_abort_if_already_setup(hass, tuya):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
assert result["reason"] == "single_instance_allowed"
|
assert result["reason"] == RESULT_SINGLE_INSTANCE
|
||||||
|
|
||||||
# Should fail, config exist (flow)
|
# Should fail, config exist (flow)
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -109,7 +132,7 @@ async def test_abort_if_already_setup(hass, tuya):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
assert result["reason"] == "single_instance_allowed"
|
assert result["reason"] == RESULT_SINGLE_INSTANCE
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_on_invalid_credentials(hass, tuya):
|
async def test_abort_on_invalid_credentials(hass, tuya):
|
||||||
|
@ -121,14 +144,14 @@ async def test_abort_on_invalid_credentials(hass, tuya):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["errors"] == {"base": "invalid_auth"}
|
assert result["errors"] == {"base": RESULT_AUTH_FAILED}
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=TUYA_USER_DATA
|
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=TUYA_USER_DATA
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
assert result["reason"] == "invalid_auth"
|
assert result["reason"] == RESULT_AUTH_FAILED
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_on_connection_error(hass, tuya):
|
async def test_abort_on_connection_error(hass, tuya):
|
||||||
|
@ -140,11 +163,143 @@ async def test_abort_on_connection_error(hass, tuya):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
assert result["reason"] == "cannot_connect"
|
assert result["reason"] == RESULT_CONN_ERROR
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=TUYA_USER_DATA
|
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=TUYA_USER_DATA
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
assert result["reason"] == "cannot_connect"
|
assert result["reason"] == RESULT_CONN_ERROR
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow(hass):
|
||||||
|
"""Test config flow options."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data=TUYA_USER_DATA,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Test check for integration not loaded
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == RESULT_CONN_ERROR
|
||||||
|
|
||||||
|
# Load integration and enter options
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
hass.data[DOMAIN] = {TUYA_DATA: MockTuya()}
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
|
# Test dev not found error
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={CONF_LIST_DEVICES: [f"light-{LIGHT_ID_FAKE1}"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
assert result["errors"] == {"base": ERROR_DEV_NOT_FOUND}
|
||||||
|
|
||||||
|
# Test dev type error
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={CONF_LIST_DEVICES: [f"light-{LIGHT_ID_FAKE2}"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
assert result["errors"] == {"base": ERROR_DEV_NOT_CONFIG}
|
||||||
|
|
||||||
|
# Test multi dev error
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={CONF_LIST_DEVICES: [f"climate-{CLIMATE_ID}", f"light-{LIGHT_ID}"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
assert result["errors"] == {"base": ERROR_DEV_MULTI_TYPE}
|
||||||
|
|
||||||
|
# Test climate options form
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"], user_input={CONF_LIST_DEVICES: [f"climate-{CLIMATE_ID}"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "device"
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||||
|
CONF_TEMP_DIVIDER: 10,
|
||||||
|
CONF_CURR_TEMP_DIVIDER: 5,
|
||||||
|
CONF_SET_TEMP_DIVIDED: False,
|
||||||
|
CONF_TEMP_STEP_OVERRIDE: STEP_HALVES,
|
||||||
|
CONF_MIN_TEMP: 12,
|
||||||
|
CONF_MAX_TEMP: 22,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
|
# Test light options form
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"], user_input={CONF_LIST_DEVICES: [f"light-{LIGHT_ID}"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "device"
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
CONF_SUPPORT_COLOR: True,
|
||||||
|
CONF_BRIGHTNESS_RANGE_MODE: 1,
|
||||||
|
CONF_MIN_KELVIN: 4000,
|
||||||
|
CONF_MAX_KELVIN: 5000,
|
||||||
|
CONF_TUYA_MAX_COLTEMP: 12000,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
|
# Test common options
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
CONF_DISCOVERY_INTERVAL: 100,
|
||||||
|
CONF_QUERY_INTERVAL: 50,
|
||||||
|
CONF_QUERY_DEVICE: LIGHT_ID,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify results
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
climate_options = config_entry.options[CLIMATE_ID]
|
||||||
|
assert climate_options[CONF_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS
|
||||||
|
assert climate_options[CONF_TEMP_DIVIDER] == 10
|
||||||
|
assert climate_options[CONF_CURR_TEMP_DIVIDER] == 5
|
||||||
|
assert climate_options[CONF_SET_TEMP_DIVIDED] is False
|
||||||
|
assert climate_options[CONF_TEMP_STEP_OVERRIDE] == STEP_HALVES
|
||||||
|
assert climate_options[CONF_MIN_TEMP] == 12
|
||||||
|
assert climate_options[CONF_MAX_TEMP] == 22
|
||||||
|
|
||||||
|
light_options = config_entry.options[LIGHT_ID]
|
||||||
|
assert light_options[CONF_SUPPORT_COLOR] is True
|
||||||
|
assert light_options[CONF_BRIGHTNESS_RANGE_MODE] == 1
|
||||||
|
assert light_options[CONF_MIN_KELVIN] == 4000
|
||||||
|
assert light_options[CONF_MAX_KELVIN] == 5000
|
||||||
|
assert light_options[CONF_TUYA_MAX_COLTEMP] == 12000
|
||||||
|
|
||||||
|
assert config_entry.options[CONF_DISCOVERY_INTERVAL] == 100
|
||||||
|
assert config_entry.options[CONF_QUERY_INTERVAL] == 50
|
||||||
|
assert config_entry.options[CONF_QUERY_DEVICE] == LIGHT_ID
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue