Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions homeassistant/components/tplink/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asyncio
from datetime import timedelta
import logging
import re
import time
from typing import Any, NamedTuple, cast

Expand Down Expand Up @@ -60,6 +61,21 @@
MAX_ATTEMPTS = 300
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):
"""Set up lights."""
Expand Down Expand Up @@ -269,6 +285,19 @@ def supported_features(self):
"""Flag 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
Comment thread
frenck marked this conversation as resolved.
# 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):
"""Determine all supported features in one go."""
sysinfo = self.smartbulb.sys_info
Expand All @@ -285,9 +314,7 @@ def _get_light_features(self):
supported_features += SUPPORT_BRIGHTNESS
if sysinfo.get(LIGHT_SYSINFO_IS_VARIABLE_COLOR_TEMP):
supported_features += SUPPORT_COLOR_TEMP
# Have to make another api request here in
# order to not re-implement pyHS100 here
max_range, min_range = self.smartbulb.valid_temperature_range
max_range, min_range = self._get_valid_temperature_range()
min_mireds = kelvin_to_mired(min_range)
max_mireds = kelvin_to_mired(max_range)
if sysinfo.get(LIGHT_SYSINFO_IS_COLOR):
Expand Down
135 changes: 135 additions & 0 deletions tests/components/tplink/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,116 @@ class SmartSwitchMockData(NamedTuple):
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")
def light_mock_data_fixture() -> None:
"""Create light mock data."""
Expand Down Expand Up @@ -343,6 +453,31 @@ async def test_smartswitch(
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:
"""Test function."""
light_state = light_mock_data.light_state
Expand Down