mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Fix senseme fan lights (#65217)
This commit is contained in:
parent
7552404f70
commit
2f6bf08165
@ -31,50 +31,30 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up SenseME lights."""
|
"""Set up SenseME lights."""
|
||||||
device = hass.data[DOMAIN][entry.entry_id]
|
device = hass.data[DOMAIN][entry.entry_id]
|
||||||
if device.has_light:
|
if not device.has_light:
|
||||||
async_add_entities([HASensemeLight(device)])
|
return
|
||||||
|
if device.is_light:
|
||||||
|
async_add_entities([HASensemeStandaloneLight(device)])
|
||||||
|
else:
|
||||||
|
async_add_entities([HASensemeFanLight(device)])
|
||||||
|
|
||||||
|
|
||||||
class HASensemeLight(SensemeEntity, LightEntity):
|
class HASensemeLight(SensemeEntity, LightEntity):
|
||||||
"""Representation of a Big Ass Fans SenseME light."""
|
"""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."""
|
"""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)
|
super().__init__(device, name)
|
||||||
if device.is_light:
|
self._attr_unique_id = f"{device.uuid}-LIGHT" # for legacy compat
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Update attrs from device."""
|
"""Update attrs from device."""
|
||||||
self._attr_is_on = self._device.light_on
|
self._attr_is_on = self._device.light_on
|
||||||
self._attr_brightness = int(min(255, self._device.light_brightness * 16))
|
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:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on the light."""
|
"""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:
|
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None:
|
||||||
# set the brightness, which will also turn on/off light
|
# set the brightness, which will also turn on/off light
|
||||||
if brightness == 255:
|
if brightness == 255:
|
||||||
@ -86,3 +66,45 @@ class HASensemeLight(SensemeEntity, LightEntity):
|
|||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the light."""
|
"""Turn off the light."""
|
||||||
self._device.light_on = False
|
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,22 +12,27 @@ MOCK_UUID = "77a6b7b3-925d-4695-a415-76d76dca4444"
|
|||||||
MOCK_ADDRESS = "127.0.0.1"
|
MOCK_ADDRESS = "127.0.0.1"
|
||||||
MOCK_MAC = "20:F8:5E:92:5A:75"
|
MOCK_MAC = "20:F8:5E:92:5A:75"
|
||||||
|
|
||||||
device = MagicMock(auto_spec=SensemeDevice)
|
|
||||||
device.async_update = AsyncMock()
|
def _mock_device():
|
||||||
device.model = "Haiku Fan"
|
device = MagicMock(auto_spec=SensemeDevice)
|
||||||
device.fan_speed_max = 7
|
device.async_update = AsyncMock()
|
||||||
device.mac = "aa:bb:cc:dd:ee:ff"
|
device.model = "Haiku Fan"
|
||||||
device.fan_dir = "REV"
|
device.fan_speed_max = 7
|
||||||
device.room_name = "Main"
|
device.mac = "aa:bb:cc:dd:ee:ff"
|
||||||
device.room_type = "Main"
|
device.fan_dir = "REV"
|
||||||
device.fw_version = "1"
|
device.has_light = True
|
||||||
device.fan_autocomfort = "on"
|
device.is_light = False
|
||||||
device.fan_smartmode = "on"
|
device.light_brightness = 50
|
||||||
device.fan_whoosh_mode = "on"
|
device.room_name = "Main"
|
||||||
device.name = MOCK_NAME
|
device.room_type = "Main"
|
||||||
device.uuid = MOCK_UUID
|
device.fw_version = "1"
|
||||||
device.address = MOCK_ADDRESS
|
device.fan_autocomfort = "COOLING"
|
||||||
device.get_device_info = {
|
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,
|
"name": MOCK_NAME,
|
||||||
"uuid": MOCK_UUID,
|
"uuid": MOCK_UUID,
|
||||||
"mac": MOCK_ADDRESS,
|
"mac": MOCK_ADDRESS,
|
||||||
@ -37,7 +42,8 @@ device.get_device_info = {
|
|||||||
"has_sensor": True,
|
"has_sensor": True,
|
||||||
"is_fan": True,
|
"is_fan": True,
|
||||||
"is_light": False,
|
"is_light": False,
|
||||||
}
|
}
|
||||||
|
return device
|
||||||
|
|
||||||
|
|
||||||
device_alternate_ip = MagicMock(auto_spec=SensemeDevice)
|
device_alternate_ip = MagicMock(auto_spec=SensemeDevice)
|
||||||
@ -99,7 +105,7 @@ device_no_uuid = MagicMock(auto_spec=SensemeDevice)
|
|||||||
device_no_uuid.uuid = None
|
device_no_uuid.uuid = None
|
||||||
|
|
||||||
|
|
||||||
MOCK_DEVICE = device
|
MOCK_DEVICE = _mock_device()
|
||||||
MOCK_DEVICE_ALTERNATE_IP = device_alternate_ip
|
MOCK_DEVICE_ALTERNATE_IP = device_alternate_ip
|
||||||
MOCK_DEVICE2 = device2
|
MOCK_DEVICE2 = device2
|
||||||
MOCK_DEVICE_NO_UUID = device_no_uuid
|
MOCK_DEVICE_NO_UUID = device_no_uuid
|
||||||
@ -121,3 +127,17 @@ def _patch_discovery(device=None, no_device=None):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
return _patcher()
|
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,
|
||||||
|
)
|
||||||
|
103
tests/components/senseme/test_light.py
Normal file
103
tests/components/senseme/test_light.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user