Fix senseme fan lights (#65217)
parent
7552404f70
commit
2f6bf08165
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue