Fix senseme fan lights (#65217)

pull/65284/head
J. Nick Koston 2022-01-30 22:24:42 -06:00 committed by GitHub
parent 7552404f70
commit 2f6bf08165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 200 additions and 55 deletions

View File

@ -31,50 +31,30 @@ async def async_setup_entry(
) -> None:
"""Set up SenseME lights."""
device = hass.data[DOMAIN][entry.entry_id]
if device.has_light:
async_add_entities([HASensemeLight(device)])
if not device.has_light:
return
if device.is_light:
async_add_entities([HASensemeStandaloneLight(device)])
else:
async_add_entities([HASensemeFanLight(device)])
class HASensemeLight(SensemeEntity, LightEntity):
"""Representation of a Big Ass Fans SenseME light."""
def __init__(self, device: SensemeDevice) -> None:
def __init__(self, device: SensemeDevice, name: str) -> None:
"""Initialize the entity."""
self._device = device
if device.is_light:
name = device.name # The device itself is a light
else:
name = f"{device.name} Light" # A fan light
super().__init__(device, name)
if device.is_light:
self._attr_supported_color_modes = {COLOR_MODE_COLOR_TEMP}
self._attr_color_mode = COLOR_MODE_COLOR_TEMP
else:
self._attr_supported_color_modes = {COLOR_MODE_BRIGHTNESS}
self._attr_color_mode = COLOR_MODE_BRIGHTNESS
self._attr_unique_id = f"{self._device.uuid}-LIGHT" # for legacy compat
self._attr_min_mireds = color_temperature_kelvin_to_mired(
self._device.light_color_temp_max
)
self._attr_max_mireds = color_temperature_kelvin_to_mired(
self._device.light_color_temp_min
)
self._attr_unique_id = f"{device.uuid}-LIGHT" # for legacy compat
@callback
def _async_update_attrs(self) -> None:
"""Update attrs from device."""
self._attr_is_on = self._device.light_on
self._attr_brightness = int(min(255, self._device.light_brightness * 16))
self._attr_color_temp = color_temperature_kelvin_to_mired(
self._device.light_color_temp
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None:
self._device.light_color_temp = color_temperature_mired_to_kelvin(
color_temp
)
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None:
# set the brightness, which will also turn on/off light
if brightness == 255:
@ -86,3 +66,45 @@ class HASensemeLight(SensemeEntity, LightEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
self._device.light_on = False
class HASensemeFanLight(HASensemeLight):
"""Representation of a Big Ass Fans SenseME light on a fan."""
def __init__(self, device: SensemeDevice) -> None:
"""Init a fan light."""
super().__init__(device, device.name)
self._attr_supported_color_modes = {COLOR_MODE_BRIGHTNESS}
self._attr_color_mode = COLOR_MODE_BRIGHTNESS
class HASensemeStandaloneLight(HASensemeLight):
"""Representation of a Big Ass Fans SenseME light."""
def __init__(self, device: SensemeDevice) -> None:
"""Init a standalone light."""
super().__init__(device, f"{device.name} Light")
self._attr_supported_color_modes = {COLOR_MODE_COLOR_TEMP}
self._attr_color_mode = COLOR_MODE_COLOR_TEMP
self._attr_min_mireds = color_temperature_kelvin_to_mired(
device.light_color_temp_max
)
self._attr_max_mireds = color_temperature_kelvin_to_mired(
device.light_color_temp_min
)
@callback
def _async_update_attrs(self) -> None:
"""Update attrs from device."""
super()._async_update_attrs()
self._attr_color_temp = color_temperature_kelvin_to_mired(
self._device.light_color_temp
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None:
self._device.light_color_temp = color_temperature_mired_to_kelvin(
color_temp
)
await super().async_turn_on(**kwargs)

View File

@ -12,32 +12,38 @@ MOCK_UUID = "77a6b7b3-925d-4695-a415-76d76dca4444"
MOCK_ADDRESS = "127.0.0.1"
MOCK_MAC = "20:F8:5E:92:5A:75"
device = MagicMock(auto_spec=SensemeDevice)
device.async_update = AsyncMock()
device.model = "Haiku Fan"
device.fan_speed_max = 7
device.mac = "aa:bb:cc:dd:ee:ff"
device.fan_dir = "REV"
device.room_name = "Main"
device.room_type = "Main"
device.fw_version = "1"
device.fan_autocomfort = "on"
device.fan_smartmode = "on"
device.fan_whoosh_mode = "on"
device.name = MOCK_NAME
device.uuid = MOCK_UUID
device.address = MOCK_ADDRESS
device.get_device_info = {
"name": MOCK_NAME,
"uuid": MOCK_UUID,
"mac": MOCK_ADDRESS,
"address": MOCK_ADDRESS,
"base_model": "FAN,HAIKU,HSERIES",
"has_light": False,
"has_sensor": True,
"is_fan": True,
"is_light": False,
}
def _mock_device():
device = MagicMock(auto_spec=SensemeDevice)
device.async_update = AsyncMock()
device.model = "Haiku Fan"
device.fan_speed_max = 7
device.mac = "aa:bb:cc:dd:ee:ff"
device.fan_dir = "REV"
device.has_light = True
device.is_light = False
device.light_brightness = 50
device.room_name = "Main"
device.room_type = "Main"
device.fw_version = "1"
device.fan_autocomfort = "COOLING"
device.fan_smartmode = "OFF"
device.fan_whoosh_mode = "on"
device.name = MOCK_NAME
device.uuid = MOCK_UUID
device.address = MOCK_ADDRESS
device.get_device_info = {
"name": MOCK_NAME,
"uuid": MOCK_UUID,
"mac": MOCK_ADDRESS,
"address": MOCK_ADDRESS,
"base_model": "FAN,HAIKU,HSERIES",
"has_light": False,
"has_sensor": True,
"is_fan": True,
"is_light": False,
}
return device
device_alternate_ip = MagicMock(auto_spec=SensemeDevice)
@ -99,7 +105,7 @@ device_no_uuid = MagicMock(auto_spec=SensemeDevice)
device_no_uuid.uuid = None
MOCK_DEVICE = device
MOCK_DEVICE = _mock_device()
MOCK_DEVICE_ALTERNATE_IP = device_alternate_ip
MOCK_DEVICE2 = device2
MOCK_DEVICE_NO_UUID = device_no_uuid
@ -121,3 +127,17 @@ def _patch_discovery(device=None, no_device=None):
yield
return _patcher()
def _patch_device(device=None, no_device=False):
async def _device_mocker(*args, **kwargs):
if no_device:
return False, None
if device:
return True, device
return True, _mock_device()
return patch(
"homeassistant.components.senseme.async_get_device_by_device_info",
new=_device_mocker,
)

View File

@ -0,0 +1,103 @@
"""Tests for senseme light platform."""
from aiosenseme import SensemeDevice
from homeassistant.components import senseme
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_COLOR_TEMP,
ATTR_SUPPORTED_COLOR_MODES,
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
DOMAIN as LIGHT_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.components.senseme.const import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from . import _mock_device, _patch_device, _patch_discovery
from tests.common import MockConfigEntry
async def _setup_mocked_entry(hass: HomeAssistant, device: SensemeDevice) -> None:
"""Set up a mocked entry."""
entry = MockConfigEntry(
domain=DOMAIN,
data={"info": device.get_device_info},
unique_id=device.uuid,
)
entry.add_to_hass(hass)
with _patch_discovery(), _patch_device(device=device):
await async_setup_component(hass, senseme.DOMAIN, {senseme.DOMAIN: {}})
await hass.async_block_till_done()
async def test_light_unique_id(hass: HomeAssistant) -> None:
"""Test a light unique id."""
device = _mock_device()
await _setup_mocked_entry(hass, device)
entity_id = "light.haiku_fan"
entity_registry = er.async_get(hass)
assert entity_registry.async_get(entity_id).unique_id == f"{device.uuid}-LIGHT"
state = hass.states.get(entity_id)
assert state.state == STATE_ON
async def test_fan_light(hass: HomeAssistant) -> None:
"""Test a fan light."""
device = _mock_device()
await _setup_mocked_entry(hass, device)
entity_id = "light.haiku_fan"
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attributes = state.attributes
assert attributes[ATTR_BRIGHTNESS] == 255
assert attributes[ATTR_COLOR_MODE] == COLOR_MODE_BRIGHTNESS
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [COLOR_MODE_BRIGHTNESS]
await hass.services.async_call(
LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert device.light_on is False
await hass.services.async_call(
LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert device.light_on is True
async def test_standalone_light(hass: HomeAssistant) -> None:
"""Test a standalone light."""
device = _mock_device()
device.is_light = True
device.light_color_temp_max = 6500
device.light_color_temp_min = 2700
device.light_color_temp = 4000
await _setup_mocked_entry(hass, device)
entity_id = "light.haiku_fan_light"
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attributes = state.attributes
assert attributes[ATTR_BRIGHTNESS] == 255
assert attributes[ATTR_COLOR_MODE] == COLOR_MODE_COLOR_TEMP
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == [COLOR_MODE_COLOR_TEMP]
assert attributes[ATTR_COLOR_TEMP] == 250
await hass.services.async_call(
LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert device.light_on is False
await hass.services.async_call(
LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
assert device.light_on is True