mirror of
https://github.com/home-assistant/core.git
synced 2025-04-29 11:47:50 +00:00
Avoid divide by zero errors in tplink light integration (#48235)
This commit is contained in:
parent
9e1a17c62e
commit
9fd6980144
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Any, NamedTuple, cast
|
from typing import Any, NamedTuple, cast
|
||||||
|
|
||||||
@ -60,6 +61,21 @@ LIGHT_SYSINFO_IS_COLOR = "is_color"
|
|||||||
MAX_ATTEMPTS = 300
|
MAX_ATTEMPTS = 300
|
||||||
SLEEP_TIME = 2
|
SLEEP_TIME = 2
|
||||||
|
|
||||||
|
TPLINK_KELVIN = {
|
||||||
|
"LB130": (2500, 9000),
|
||||||
|
"LB120": (2700, 6500),
|
||||||
|
"LB230": (2500, 9000),
|
||||||
|
"KB130": (2500, 9000),
|
||||||
|
"KL130": (2500, 9000),
|
||||||
|
"KL125": (2500, 6500),
|
||||||
|
r"KL120\(EU\)": (2700, 6500),
|
||||||
|
r"KL120\(US\)": (2700, 5000),
|
||||||
|
r"KL430\(US\)": (2500, 9000),
|
||||||
|
}
|
||||||
|
|
||||||
|
FALLBACK_MIN_COLOR = 2700
|
||||||
|
FALLBACK_MAX_COLOR = 5000
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities):
|
async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities):
|
||||||
"""Set up lights."""
|
"""Set up lights."""
|
||||||
@ -269,6 +285,19 @@ class TPLinkSmartBulb(LightEntity):
|
|||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return self._light_features.supported_features
|
return self._light_features.supported_features
|
||||||
|
|
||||||
|
def _get_valid_temperature_range(self):
|
||||||
|
"""Return the device-specific white temperature range (in Kelvin).
|
||||||
|
|
||||||
|
:return: White temperature range in Kelvin (minimum, maximum)
|
||||||
|
"""
|
||||||
|
model = self.smartbulb.sys_info[LIGHT_SYSINFO_MODEL]
|
||||||
|
for obj, temp_range in TPLINK_KELVIN.items():
|
||||||
|
if re.match(obj, model):
|
||||||
|
return temp_range
|
||||||
|
# pyHS100 is abandoned, but some bulb definitions aren't present
|
||||||
|
# use "safe" values for something that advertises color temperature
|
||||||
|
return FALLBACK_MIN_COLOR, FALLBACK_MAX_COLOR
|
||||||
|
|
||||||
def _get_light_features(self):
|
def _get_light_features(self):
|
||||||
"""Determine all supported features in one go."""
|
"""Determine all supported features in one go."""
|
||||||
sysinfo = self.smartbulb.sys_info
|
sysinfo = self.smartbulb.sys_info
|
||||||
@ -285,9 +314,7 @@ class TPLinkSmartBulb(LightEntity):
|
|||||||
supported_features += SUPPORT_BRIGHTNESS
|
supported_features += SUPPORT_BRIGHTNESS
|
||||||
if sysinfo.get(LIGHT_SYSINFO_IS_VARIABLE_COLOR_TEMP):
|
if sysinfo.get(LIGHT_SYSINFO_IS_VARIABLE_COLOR_TEMP):
|
||||||
supported_features += SUPPORT_COLOR_TEMP
|
supported_features += SUPPORT_COLOR_TEMP
|
||||||
# Have to make another api request here in
|
max_range, min_range = self._get_valid_temperature_range()
|
||||||
# order to not re-implement pyHS100 here
|
|
||||||
max_range, min_range = self.smartbulb.valid_temperature_range
|
|
||||||
min_mireds = kelvin_to_mired(min_range)
|
min_mireds = kelvin_to_mired(min_range)
|
||||||
max_mireds = kelvin_to_mired(max_range)
|
max_mireds = kelvin_to_mired(max_range)
|
||||||
if sysinfo.get(LIGHT_SYSINFO_IS_COLOR):
|
if sysinfo.get(LIGHT_SYSINFO_IS_COLOR):
|
||||||
|
@ -60,6 +60,116 @@ class SmartSwitchMockData(NamedTuple):
|
|||||||
get_sysinfo_mock: Mock
|
get_sysinfo_mock: Mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="unknown_light_mock_data")
|
||||||
|
def unknown_light_mock_data_fixture() -> None:
|
||||||
|
"""Create light mock data."""
|
||||||
|
sys_info = {
|
||||||
|
"sw_ver": "1.2.3",
|
||||||
|
"hw_ver": "2.3.4",
|
||||||
|
"mac": "aa:bb:cc:dd:ee:ff",
|
||||||
|
"mic_mac": "00:11:22:33:44",
|
||||||
|
"type": "light",
|
||||||
|
"hwId": "1234",
|
||||||
|
"fwId": "4567",
|
||||||
|
"oemId": "891011",
|
||||||
|
"dev_name": "light1",
|
||||||
|
"rssi": 11,
|
||||||
|
"latitude": "0",
|
||||||
|
"longitude": "0",
|
||||||
|
"is_color": True,
|
||||||
|
"is_dimmable": True,
|
||||||
|
"is_variable_color_temp": True,
|
||||||
|
"model": "Foo",
|
||||||
|
"alias": "light1",
|
||||||
|
}
|
||||||
|
light_state = {
|
||||||
|
"on_off": True,
|
||||||
|
"dft_on_state": {
|
||||||
|
"brightness": 12,
|
||||||
|
"color_temp": 3200,
|
||||||
|
"hue": 110,
|
||||||
|
"saturation": 90,
|
||||||
|
},
|
||||||
|
"brightness": 13,
|
||||||
|
"color_temp": 3300,
|
||||||
|
"hue": 110,
|
||||||
|
"saturation": 90,
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_light_state(state) -> None:
|
||||||
|
nonlocal light_state
|
||||||
|
drt_on_state = light_state["dft_on_state"]
|
||||||
|
drt_on_state.update(state.get("dft_on_state", {}))
|
||||||
|
|
||||||
|
light_state.update(state)
|
||||||
|
light_state["dft_on_state"] = drt_on_state
|
||||||
|
return light_state
|
||||||
|
|
||||||
|
set_light_state_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartBulb.set_light_state",
|
||||||
|
side_effect=set_light_state,
|
||||||
|
)
|
||||||
|
get_light_state_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartBulb.get_light_state",
|
||||||
|
return_value=light_state,
|
||||||
|
)
|
||||||
|
current_consumption_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartDevice.current_consumption",
|
||||||
|
return_value=3.23,
|
||||||
|
)
|
||||||
|
get_sysinfo_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartDevice.get_sysinfo",
|
||||||
|
return_value=sys_info,
|
||||||
|
)
|
||||||
|
get_emeter_daily_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartDevice.get_emeter_daily",
|
||||||
|
return_value={
|
||||||
|
1: 1.01,
|
||||||
|
2: 1.02,
|
||||||
|
3: 1.03,
|
||||||
|
4: 1.04,
|
||||||
|
5: 1.05,
|
||||||
|
6: 1.06,
|
||||||
|
7: 1.07,
|
||||||
|
8: 1.08,
|
||||||
|
9: 1.09,
|
||||||
|
10: 1.10,
|
||||||
|
11: 1.11,
|
||||||
|
12: 1.12,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
get_emeter_monthly_patch = patch(
|
||||||
|
"homeassistant.components.tplink.common.SmartDevice.get_emeter_monthly",
|
||||||
|
return_value={
|
||||||
|
1: 2.01,
|
||||||
|
2: 2.02,
|
||||||
|
3: 2.03,
|
||||||
|
4: 2.04,
|
||||||
|
5: 2.05,
|
||||||
|
6: 2.06,
|
||||||
|
7: 2.07,
|
||||||
|
8: 2.08,
|
||||||
|
9: 2.09,
|
||||||
|
10: 2.10,
|
||||||
|
11: 2.11,
|
||||||
|
12: 2.12,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with set_light_state_patch as set_light_state_mock, get_light_state_patch as get_light_state_mock, current_consumption_patch as current_consumption_mock, get_sysinfo_patch as get_sysinfo_mock, get_emeter_daily_patch as get_emeter_daily_mock, get_emeter_monthly_patch as get_emeter_monthly_mock:
|
||||||
|
yield LightMockData(
|
||||||
|
sys_info=sys_info,
|
||||||
|
light_state=light_state,
|
||||||
|
set_light_state=set_light_state,
|
||||||
|
set_light_state_mock=set_light_state_mock,
|
||||||
|
get_light_state_mock=get_light_state_mock,
|
||||||
|
current_consumption_mock=current_consumption_mock,
|
||||||
|
get_sysinfo_mock=get_sysinfo_mock,
|
||||||
|
get_emeter_daily_mock=get_emeter_daily_mock,
|
||||||
|
get_emeter_monthly_mock=get_emeter_monthly_mock,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="light_mock_data")
|
@pytest.fixture(name="light_mock_data")
|
||||||
def light_mock_data_fixture() -> None:
|
def light_mock_data_fixture() -> None:
|
||||||
"""Create light mock data."""
|
"""Create light mock data."""
|
||||||
@ -343,6 +453,31 @@ async def test_smartswitch(
|
|||||||
assert sys_info["brightness"] == 66
|
assert sys_info["brightness"] == 66
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unknown_light(
|
||||||
|
hass: HomeAssistant, unknown_light_mock_data: LightMockData
|
||||||
|
) -> None:
|
||||||
|
"""Test function."""
|
||||||
|
await async_setup_component(hass, HA_DOMAIN, {})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await async_setup_component(
|
||||||
|
hass,
|
||||||
|
tplink.DOMAIN,
|
||||||
|
{
|
||||||
|
tplink.DOMAIN: {
|
||||||
|
CONF_DISCOVERY: False,
|
||||||
|
CONF_LIGHT: [{CONF_HOST: "123.123.123.123"}],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("light.light1")
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes["min_mireds"] == 200
|
||||||
|
assert state.attributes["max_mireds"] == 370
|
||||||
|
|
||||||
|
|
||||||
async def test_light(hass: HomeAssistant, light_mock_data: LightMockData) -> None:
|
async def test_light(hass: HomeAssistant, light_mock_data: LightMockData) -> None:
|
||||||
"""Test function."""
|
"""Test function."""
|
||||||
light_state = light_mock_data.light_state
|
light_state = light_mock_data.light_state
|
||||||
|
Loading…
x
Reference in New Issue
Block a user