mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Improve HomeKit Color with Color Temp implementation (#54371)
This commit is contained in:
parent
4bde4504ec
commit
4da451fcf7
@ -6,13 +6,11 @@ from pyhap.const import CATEGORY_LIGHTBULB
|
|||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_BRIGHTNESS_PCT,
|
ATTR_BRIGHTNESS_PCT,
|
||||||
ATTR_COLOR_MODE,
|
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_MAX_MIREDS,
|
ATTR_MAX_MIREDS,
|
||||||
ATTR_MIN_MIREDS,
|
ATTR_MIN_MIREDS,
|
||||||
ATTR_SUPPORTED_COLOR_MODES,
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
COLOR_MODE_COLOR_TEMP,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
brightness_supported,
|
brightness_supported,
|
||||||
color_supported,
|
color_supported,
|
||||||
@ -25,13 +23,17 @@ from homeassistant.const import (
|
|||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.event import async_call_later
|
||||||
|
from homeassistant.util.color import (
|
||||||
|
color_temperature_mired_to_kelvin,
|
||||||
|
color_temperature_to_hs,
|
||||||
|
)
|
||||||
|
|
||||||
from .accessories import TYPES, HomeAccessory
|
from .accessories import TYPES, HomeAccessory
|
||||||
from .const import (
|
from .const import (
|
||||||
CHAR_BRIGHTNESS,
|
CHAR_BRIGHTNESS,
|
||||||
CHAR_COLOR_TEMPERATURE,
|
CHAR_COLOR_TEMPERATURE,
|
||||||
CHAR_HUE,
|
CHAR_HUE,
|
||||||
CHAR_NAME,
|
|
||||||
CHAR_ON,
|
CHAR_ON,
|
||||||
CHAR_SATURATION,
|
CHAR_SATURATION,
|
||||||
PROP_MAX_VALUE,
|
PROP_MAX_VALUE,
|
||||||
@ -43,6 +45,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
RGB_COLOR = "rgb_color"
|
RGB_COLOR = "rgb_color"
|
||||||
|
|
||||||
|
CHANGE_COALESCE_TIME_WINDOW = 0.01
|
||||||
|
|
||||||
|
|
||||||
@TYPES.register("Light")
|
@TYPES.register("Light")
|
||||||
class Light(HomeAccessory):
|
class Light(HomeAccessory):
|
||||||
@ -55,102 +59,78 @@ class Light(HomeAccessory):
|
|||||||
"""Initialize a new Light accessory object."""
|
"""Initialize a new Light accessory object."""
|
||||||
super().__init__(*args, category=CATEGORY_LIGHTBULB)
|
super().__init__(*args, category=CATEGORY_LIGHTBULB)
|
||||||
|
|
||||||
self.chars_primary = []
|
self.chars = []
|
||||||
self.chars_secondary = []
|
self._event_timer = None
|
||||||
|
self._pending_events = {}
|
||||||
|
|
||||||
state = self.hass.states.get(self.entity_id)
|
state = self.hass.states.get(self.entity_id)
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
color_modes = attributes.get(ATTR_SUPPORTED_COLOR_MODES)
|
color_modes = attributes.get(ATTR_SUPPORTED_COLOR_MODES)
|
||||||
self.is_color_supported = color_supported(color_modes)
|
self.color_supported = color_supported(color_modes)
|
||||||
self.is_color_temp_supported = color_temp_supported(color_modes)
|
self.color_temp_supported = color_temp_supported(color_modes)
|
||||||
self.color_and_temp_supported = (
|
self.brightness_supported = brightness_supported(color_modes)
|
||||||
self.is_color_supported and self.is_color_temp_supported
|
|
||||||
)
|
|
||||||
self.is_brightness_supported = brightness_supported(color_modes)
|
|
||||||
|
|
||||||
if self.is_brightness_supported:
|
if self.brightness_supported:
|
||||||
self.chars_primary.append(CHAR_BRIGHTNESS)
|
self.chars.append(CHAR_BRIGHTNESS)
|
||||||
|
|
||||||
if self.is_color_supported:
|
if self.color_supported:
|
||||||
self.chars_primary.append(CHAR_HUE)
|
self.chars.extend([CHAR_HUE, CHAR_SATURATION])
|
||||||
self.chars_primary.append(CHAR_SATURATION)
|
|
||||||
|
|
||||||
if self.is_color_temp_supported:
|
if self.color_temp_supported:
|
||||||
if self.color_and_temp_supported:
|
self.chars.append(CHAR_COLOR_TEMPERATURE)
|
||||||
self.chars_primary.append(CHAR_NAME)
|
|
||||||
self.chars_secondary.append(CHAR_NAME)
|
|
||||||
self.chars_secondary.append(CHAR_COLOR_TEMPERATURE)
|
|
||||||
if self.is_brightness_supported:
|
|
||||||
self.chars_secondary.append(CHAR_BRIGHTNESS)
|
|
||||||
else:
|
|
||||||
self.chars_primary.append(CHAR_COLOR_TEMPERATURE)
|
|
||||||
|
|
||||||
serv_light_primary = self.add_preload_service(
|
serv_light = self.add_preload_service(SERV_LIGHTBULB, self.chars)
|
||||||
SERV_LIGHTBULB, self.chars_primary
|
self.char_on = serv_light.configure_char(CHAR_ON, value=0)
|
||||||
)
|
|
||||||
serv_light_secondary = None
|
|
||||||
self.char_on_primary = serv_light_primary.configure_char(CHAR_ON, value=0)
|
|
||||||
|
|
||||||
if self.color_and_temp_supported:
|
if self.brightness_supported:
|
||||||
serv_light_secondary = self.add_preload_service(
|
|
||||||
SERV_LIGHTBULB, self.chars_secondary
|
|
||||||
)
|
|
||||||
serv_light_primary.add_linked_service(serv_light_secondary)
|
|
||||||
serv_light_primary.configure_char(CHAR_NAME, value="RGB")
|
|
||||||
self.char_on_secondary = serv_light_secondary.configure_char(
|
|
||||||
CHAR_ON, value=0
|
|
||||||
)
|
|
||||||
serv_light_secondary.configure_char(CHAR_NAME, value="Temperature")
|
|
||||||
|
|
||||||
if self.is_brightness_supported:
|
|
||||||
# Initial value is set to 100 because 0 is a special value (off). 100 is
|
# Initial value is set to 100 because 0 is a special value (off). 100 is
|
||||||
# an arbitrary non-zero value. It is updated immediately by async_update_state
|
# an arbitrary non-zero value. It is updated immediately by async_update_state
|
||||||
# to set to the correct initial value.
|
# to set to the correct initial value.
|
||||||
self.char_brightness_primary = serv_light_primary.configure_char(
|
self.char_brightness = serv_light.configure_char(CHAR_BRIGHTNESS, value=100)
|
||||||
CHAR_BRIGHTNESS, value=100
|
|
||||||
)
|
|
||||||
if self.chars_secondary:
|
|
||||||
self.char_brightness_secondary = serv_light_secondary.configure_char(
|
|
||||||
CHAR_BRIGHTNESS, value=100
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.is_color_temp_supported:
|
if self.color_temp_supported:
|
||||||
min_mireds = attributes.get(ATTR_MIN_MIREDS, 153)
|
min_mireds = attributes.get(ATTR_MIN_MIREDS, 153)
|
||||||
max_mireds = attributes.get(ATTR_MAX_MIREDS, 500)
|
max_mireds = attributes.get(ATTR_MAX_MIREDS, 500)
|
||||||
serv_light = serv_light_secondary or serv_light_primary
|
self.char_color_temp = serv_light.configure_char(
|
||||||
self.char_color_temperature = serv_light.configure_char(
|
|
||||||
CHAR_COLOR_TEMPERATURE,
|
CHAR_COLOR_TEMPERATURE,
|
||||||
value=min_mireds,
|
value=min_mireds,
|
||||||
properties={PROP_MIN_VALUE: min_mireds, PROP_MAX_VALUE: max_mireds},
|
properties={PROP_MIN_VALUE: min_mireds, PROP_MAX_VALUE: max_mireds},
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.is_color_supported:
|
if self.color_supported:
|
||||||
self.char_hue = serv_light_primary.configure_char(CHAR_HUE, value=0)
|
self.char_hue = serv_light.configure_char(CHAR_HUE, value=0)
|
||||||
self.char_saturation = serv_light_primary.configure_char(
|
self.char_saturation = serv_light.configure_char(CHAR_SATURATION, value=75)
|
||||||
CHAR_SATURATION, value=75
|
|
||||||
)
|
|
||||||
|
|
||||||
self.async_update_state(state)
|
self.async_update_state(state)
|
||||||
|
serv_light.setter_callback = self._set_chars
|
||||||
|
|
||||||
if self.color_and_temp_supported:
|
def _set_chars(self, char_values):
|
||||||
serv_light_primary.setter_callback = self._set_chars_primary
|
_LOGGER.debug("Light _set_chars: %s", char_values)
|
||||||
serv_light_secondary.setter_callback = self._set_chars_secondary
|
# Newest change always wins
|
||||||
else:
|
if CHAR_COLOR_TEMPERATURE in self._pending_events and (
|
||||||
serv_light_primary.setter_callback = self._set_chars
|
CHAR_SATURATION in char_values or CHAR_HUE in char_values
|
||||||
|
):
|
||||||
|
del self._pending_events[CHAR_COLOR_TEMPERATURE]
|
||||||
|
for char in (CHAR_HUE, CHAR_SATURATION):
|
||||||
|
if char in self._pending_events and CHAR_COLOR_TEMPERATURE in char_values:
|
||||||
|
del self._pending_events[char]
|
||||||
|
|
||||||
def _set_chars_primary(self, char_values):
|
self._pending_events.update(char_values)
|
||||||
"""Primary service is RGB or W if only color or color temp is supported."""
|
if self._event_timer:
|
||||||
self._set_chars(char_values, True)
|
self._event_timer()
|
||||||
|
self._event_timer = async_call_later(
|
||||||
|
self.hass, CHANGE_COALESCE_TIME_WINDOW, self._send_events
|
||||||
|
)
|
||||||
|
|
||||||
def _set_chars_secondary(self, char_values):
|
def _send_events(self, *_):
|
||||||
"""Secondary service is W if both color or color temp are supported."""
|
"""Process all changes at once."""
|
||||||
self._set_chars(char_values, False)
|
_LOGGER.debug("Coalesced _set_chars: %s", self._pending_events)
|
||||||
|
char_values = self._pending_events
|
||||||
def _set_chars(self, char_values, is_primary=None):
|
self._pending_events = {}
|
||||||
_LOGGER.debug("Light _set_chars: %s, is_primary: %s", char_values, is_primary)
|
|
||||||
events = []
|
events = []
|
||||||
service = SERVICE_TURN_ON
|
service = SERVICE_TURN_ON
|
||||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||||
|
|
||||||
if CHAR_ON in char_values:
|
if CHAR_ON in char_values:
|
||||||
if not char_values[CHAR_ON]:
|
if not char_values[CHAR_ON]:
|
||||||
service = SERVICE_TURN_OFF
|
service = SERVICE_TURN_OFF
|
||||||
@ -170,24 +150,16 @@ class Light(HomeAccessory):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.is_color_temp_supported and (
|
if CHAR_COLOR_TEMPERATURE in char_values:
|
||||||
is_primary is False or CHAR_COLOR_TEMPERATURE in char_values
|
params[ATTR_COLOR_TEMP] = char_values[CHAR_COLOR_TEMPERATURE]
|
||||||
):
|
|
||||||
params[ATTR_COLOR_TEMP] = char_values.get(
|
|
||||||
CHAR_COLOR_TEMPERATURE, self.char_color_temperature.value
|
|
||||||
)
|
|
||||||
events.append(f"color temperature at {params[ATTR_COLOR_TEMP]}")
|
events.append(f"color temperature at {params[ATTR_COLOR_TEMP]}")
|
||||||
|
|
||||||
if self.is_color_supported and (
|
elif CHAR_HUE in char_values or CHAR_SATURATION in char_values:
|
||||||
is_primary is True
|
color = params[ATTR_HS_COLOR] = (
|
||||||
or (CHAR_HUE in char_values and CHAR_SATURATION in char_values)
|
|
||||||
):
|
|
||||||
color = (
|
|
||||||
char_values.get(CHAR_HUE, self.char_hue.value),
|
char_values.get(CHAR_HUE, self.char_hue.value),
|
||||||
char_values.get(CHAR_SATURATION, self.char_saturation.value),
|
char_values.get(CHAR_SATURATION, self.char_saturation.value),
|
||||||
)
|
)
|
||||||
_LOGGER.debug("%s: Set hs_color to %s", self.entity_id, color)
|
_LOGGER.debug("%s: Set hs_color to %s", self.entity_id, color)
|
||||||
params[ATTR_HS_COLOR] = color
|
|
||||||
events.append(f"set color at {color}")
|
events.append(f"set color at {color}")
|
||||||
|
|
||||||
self.async_call_service(DOMAIN, service, params, ", ".join(events))
|
self.async_call_service(DOMAIN, service, params, ", ".join(events))
|
||||||
@ -198,20 +170,10 @@ class Light(HomeAccessory):
|
|||||||
# Handle State
|
# Handle State
|
||||||
state = new_state.state
|
state = new_state.state
|
||||||
attributes = new_state.attributes
|
attributes = new_state.attributes
|
||||||
char_on_value = int(state == STATE_ON)
|
self.char_on.set_value(int(state == STATE_ON))
|
||||||
|
|
||||||
if self.color_and_temp_supported:
|
|
||||||
color_mode = attributes.get(ATTR_COLOR_MODE)
|
|
||||||
color_temp_mode = color_mode == COLOR_MODE_COLOR_TEMP
|
|
||||||
primary_on_value = char_on_value if not color_temp_mode else 0
|
|
||||||
secondary_on_value = char_on_value if color_temp_mode else 0
|
|
||||||
self.char_on_primary.set_value(primary_on_value)
|
|
||||||
self.char_on_secondary.set_value(secondary_on_value)
|
|
||||||
else:
|
|
||||||
self.char_on_primary.set_value(char_on_value)
|
|
||||||
|
|
||||||
# Handle Brightness
|
# Handle Brightness
|
||||||
if self.is_brightness_supported:
|
if self.brightness_supported:
|
||||||
brightness = attributes.get(ATTR_BRIGHTNESS)
|
brightness = attributes.get(ATTR_BRIGHTNESS)
|
||||||
if isinstance(brightness, (int, float)):
|
if isinstance(brightness, (int, float)):
|
||||||
brightness = round(brightness / 255 * 100, 0)
|
brightness = round(brightness / 255 * 100, 0)
|
||||||
@ -227,22 +189,25 @@ class Light(HomeAccessory):
|
|||||||
# order to avoid this incorrect behavior.
|
# order to avoid this incorrect behavior.
|
||||||
if brightness == 0 and state == STATE_ON:
|
if brightness == 0 and state == STATE_ON:
|
||||||
brightness = 1
|
brightness = 1
|
||||||
self.char_brightness_primary.set_value(brightness)
|
self.char_brightness.set_value(brightness)
|
||||||
if self.color_and_temp_supported:
|
|
||||||
self.char_brightness_secondary.set_value(brightness)
|
# Handle Color - color must always be set before color temperature
|
||||||
|
# or the iOS UI will not display it correctly.
|
||||||
|
if self.color_supported:
|
||||||
|
if ATTR_COLOR_TEMP in attributes:
|
||||||
|
hue, saturation = color_temperature_to_hs(
|
||||||
|
color_temperature_mired_to_kelvin(
|
||||||
|
new_state.attributes[ATTR_COLOR_TEMP]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
hue, saturation = attributes.get(ATTR_HS_COLOR, (None, None))
|
||||||
|
if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)):
|
||||||
|
self.char_hue.set_value(round(hue, 0))
|
||||||
|
self.char_saturation.set_value(round(saturation, 0))
|
||||||
|
|
||||||
# Handle color temperature
|
# Handle color temperature
|
||||||
if self.is_color_temp_supported:
|
if self.color_temp_supported:
|
||||||
color_temperature = attributes.get(ATTR_COLOR_TEMP)
|
color_temp = attributes.get(ATTR_COLOR_TEMP)
|
||||||
if isinstance(color_temperature, (int, float)):
|
if isinstance(color_temp, (int, float)):
|
||||||
color_temperature = round(color_temperature, 0)
|
self.char_color_temp.set_value(round(color_temp, 0))
|
||||||
self.char_color_temperature.set_value(color_temperature)
|
|
||||||
|
|
||||||
# Handle Color
|
|
||||||
if self.is_color_supported:
|
|
||||||
hue, saturation = attributes.get(ATTR_HS_COLOR, (None, None))
|
|
||||||
if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)):
|
|
||||||
hue = round(hue, 0)
|
|
||||||
saturation = round(saturation, 0)
|
|
||||||
self.char_hue.set_value(hue)
|
|
||||||
self.char_saturation.set_value(saturation)
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
"""Test different accessory types: Lights."""
|
"""Test different accessory types: Lights."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from pyhap.const import HAP_REPR_AID, HAP_REPR_CHARS, HAP_REPR_IID, HAP_REPR_VALUE
|
from pyhap.const import HAP_REPR_AID, HAP_REPR_CHARS, HAP_REPR_IID, HAP_REPR_VALUE
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.homekit.const import ATTR_VALUE
|
from homeassistant.components.homekit.const import ATTR_VALUE
|
||||||
from homeassistant.components.homekit.type_lights import Light
|
from homeassistant.components.homekit.type_lights import (
|
||||||
|
CHANGE_COALESCE_TIME_WINDOW,
|
||||||
|
Light,
|
||||||
|
)
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_BRIGHTNESS_PCT,
|
ATTR_BRIGHTNESS_PCT,
|
||||||
ATTR_COLOR_MODE,
|
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
ATTR_SUPPORTED_COLOR_MODES,
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
COLOR_MODE_COLOR_TEMP,
|
|
||||||
COLOR_MODE_HS,
|
|
||||||
COLOR_MODE_RGB,
|
|
||||||
COLOR_MODE_XY,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -29,8 +29,16 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import CoreState
|
from homeassistant.core import CoreState
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_fire_time_changed, async_mock_service
|
||||||
|
|
||||||
|
|
||||||
|
async def _wait_for_light_coalesce(hass):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass, dt_util.utcnow() + timedelta(seconds=CHANGE_COALESCE_TIME_WINDOW)
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
async def test_light_basic(hass, hk_driver, events):
|
async def test_light_basic(hass, hk_driver, events):
|
||||||
@ -44,45 +52,41 @@ async def test_light_basic(hass, hk_driver, events):
|
|||||||
|
|
||||||
assert acc.aid == 1
|
assert acc.aid == 1
|
||||||
assert acc.category == 5 # Lightbulb
|
assert acc.category == 5 # Lightbulb
|
||||||
assert acc.char_on_primary.value
|
assert acc.char_on.value
|
||||||
|
|
||||||
await acc.run()
|
await acc.run()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_on_primary.value == 1
|
assert acc.char_on.value == 1
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_OFF, {ATTR_SUPPORTED_FEATURES: 0})
|
hass.states.async_set(entity_id, STATE_OFF, {ATTR_SUPPORTED_FEATURES: 0})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_on_primary.value == 0
|
assert acc.char_on.value == 0
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_UNKNOWN)
|
hass.states.async_set(entity_id, STATE_UNKNOWN)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_on_primary.value == 0
|
assert acc.char_on.value == 0
|
||||||
|
|
||||||
hass.states.async_remove(entity_id)
|
hass.states.async_remove(entity_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_on_primary.value == 0
|
assert acc.char_on.value == 0
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
||||||
call_turn_off = async_mock_service(hass, DOMAIN, "turn_off")
|
call_turn_off = async_mock_service(hass, DOMAIN, "turn_off")
|
||||||
|
|
||||||
char_on_primary_iid = acc.char_on_primary.to_HAP()[HAP_REPR_IID]
|
char_on_iid = acc.char_on.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
{
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1}
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_add_executor_job(acc.char_on_primary.client_update_value, 1)
|
acc.char_on.client_update_value(1)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on
|
assert call_turn_on
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert len(events) == 1
|
assert len(events) == 1
|
||||||
@ -94,16 +98,12 @@ async def test_light_basic(hass, hk_driver, events):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
{
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 0}
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 0,
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_off
|
assert call_turn_off
|
||||||
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert len(events) == 2
|
assert len(events) == 2
|
||||||
@ -128,17 +128,17 @@ async def test_light_brightness(hass, hk_driver, events, supported_color_modes):
|
|||||||
|
|
||||||
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
||||||
# brightness to 100 when turning on a light on a freshly booted up server.
|
# brightness to 100 when turning on a light on a freshly booted up server.
|
||||||
assert acc.char_brightness_primary.value != 0
|
assert acc.char_brightness.value != 0
|
||||||
char_on_primary_iid = acc.char_on_primary.to_HAP()[HAP_REPR_IID]
|
char_on_iid = acc.char_on.to_HAP()[HAP_REPR_IID]
|
||||||
char_brightness_primary_iid = acc.char_brightness_primary.to_HAP()[HAP_REPR_IID]
|
char_brightness_iid = acc.char_brightness.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
await acc.run()
|
await acc.run()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 100
|
assert acc.char_brightness.value == 100
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 40
|
assert acc.char_brightness.value == 40
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
||||||
@ -147,21 +147,17 @@ async def test_light_brightness(hass, hk_driver, events, supported_color_modes):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_brightness_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 20,
|
HAP_REPR_VALUE: 20,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on[0]
|
assert call_turn_on[0]
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
||||||
@ -173,21 +169,17 @@ async def test_light_brightness(hass, hk_driver, events, supported_color_modes):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_brightness_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 40,
|
HAP_REPR_VALUE: 40,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on[1]
|
assert call_turn_on[1]
|
||||||
assert call_turn_on[1].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[1].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[1].data[ATTR_BRIGHTNESS_PCT] == 40
|
assert call_turn_on[1].data[ATTR_BRIGHTNESS_PCT] == 40
|
||||||
@ -199,21 +191,17 @@ async def test_light_brightness(hass, hk_driver, events, supported_color_modes):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_brightness_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 0,
|
HAP_REPR_VALUE: 0,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_off
|
assert call_turn_off
|
||||||
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert len(events) == 3
|
assert len(events) == 3
|
||||||
@ -223,24 +211,24 @@ async def test_light_brightness(hass, hk_driver, events, supported_color_modes):
|
|||||||
# in update_state
|
# in update_state
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 1
|
assert acc.char_brightness.value == 1
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 255})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 255})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 100
|
assert acc.char_brightness.value == 100
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 1
|
assert acc.char_brightness.value == 1
|
||||||
|
|
||||||
# Ensure floats are handled
|
# Ensure floats are handled
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 55.66})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 55.66})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 22
|
assert acc.char_brightness.value == 22
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 108.4})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 108.4})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 43
|
assert acc.char_brightness.value == 43
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0.0})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 0.0})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 1
|
assert acc.char_brightness.value == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_light_color_temperature(hass, hk_driver, events):
|
async def test_light_color_temperature(hass, hk_driver, events):
|
||||||
@ -256,33 +244,30 @@ async def test_light_color_temperature(hass, hk_driver, events):
|
|||||||
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
|
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
|
||||||
hk_driver.add_accessory(acc)
|
hk_driver.add_accessory(acc)
|
||||||
|
|
||||||
assert acc.char_color_temperature.value == 190
|
assert acc.char_color_temp.value == 190
|
||||||
|
|
||||||
await acc.run()
|
await acc.run()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_color_temperature.value == 190
|
assert acc.char_color_temp.value == 190
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
||||||
|
|
||||||
char_color_temperature_iid = acc.char_color_temperature.to_HAP()[HAP_REPR_IID]
|
char_color_temp_iid = acc.char_color_temp.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_color_temperature_iid,
|
HAP_REPR_IID: char_color_temp_iid,
|
||||||
HAP_REPR_VALUE: 250,
|
HAP_REPR_VALUE: 250,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_add_executor_job(
|
await _wait_for_light_coalesce(hass)
|
||||||
acc.char_color_temperature.client_update_value, 250
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert call_turn_on
|
assert call_turn_on
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[0].data[ATTR_COLOR_TEMP] == 250
|
assert call_turn_on[0].data[ATTR_COLOR_TEMP] == 250
|
||||||
@ -292,11 +277,7 @@ async def test_light_color_temperature(hass, hk_driver, events):
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"supported_color_modes",
|
"supported_color_modes",
|
||||||
[
|
[["color_temp", "hs"], ["color_temp", "rgb"], ["color_temp", "xy"]],
|
||||||
[COLOR_MODE_COLOR_TEMP, COLOR_MODE_HS],
|
|
||||||
[COLOR_MODE_COLOR_TEMP, COLOR_MODE_RGB],
|
|
||||||
[COLOR_MODE_COLOR_TEMP, COLOR_MODE_XY],
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
async def test_light_color_temperature_and_rgb_color(
|
async def test_light_color_temperature_and_rgb_color(
|
||||||
hass, hk_driver, events, supported_color_modes
|
hass, hk_driver, events, supported_color_modes
|
||||||
@ -310,93 +291,190 @@ async def test_light_color_temperature_and_rgb_color(
|
|||||||
{
|
{
|
||||||
ATTR_SUPPORTED_COLOR_MODES: supported_color_modes,
|
ATTR_SUPPORTED_COLOR_MODES: supported_color_modes,
|
||||||
ATTR_COLOR_TEMP: 190,
|
ATTR_COLOR_TEMP: 190,
|
||||||
ATTR_BRIGHTNESS: 255,
|
|
||||||
ATTR_COLOR_MODE: COLOR_MODE_RGB,
|
|
||||||
ATTR_HS_COLOR: (260, 90),
|
ATTR_HS_COLOR: (260, 90),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
|
acc = Light(hass, hk_driver, "Light", entity_id, 1, None)
|
||||||
assert acc.char_hue.value == 260
|
|
||||||
assert acc.char_saturation.value == 90
|
|
||||||
assert acc.char_on_primary.value == 1
|
|
||||||
assert acc.char_on_secondary.value == 0
|
|
||||||
assert acc.char_brightness_primary.value == 100
|
|
||||||
assert acc.char_brightness_secondary.value == 100
|
|
||||||
|
|
||||||
assert hasattr(acc, "char_color_temperature")
|
|
||||||
|
|
||||||
hass.states.async_set(
|
|
||||||
entity_id,
|
|
||||||
STATE_ON,
|
|
||||||
{
|
|
||||||
ATTR_COLOR_TEMP: 224,
|
|
||||||
ATTR_COLOR_MODE: COLOR_MODE_COLOR_TEMP,
|
|
||||||
ATTR_BRIGHTNESS: 127,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
await acc.run()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert acc.char_color_temperature.value == 224
|
|
||||||
assert acc.char_on_primary.value == 0
|
|
||||||
assert acc.char_on_secondary.value == 1
|
|
||||||
assert acc.char_brightness_primary.value == 50
|
|
||||||
assert acc.char_brightness_secondary.value == 50
|
|
||||||
|
|
||||||
hass.states.async_set(
|
|
||||||
entity_id,
|
|
||||||
STATE_ON,
|
|
||||||
{
|
|
||||||
ATTR_COLOR_TEMP: 352,
|
|
||||||
ATTR_COLOR_MODE: COLOR_MODE_COLOR_TEMP,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
await acc.run()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert acc.char_color_temperature.value == 352
|
|
||||||
assert acc.char_on_primary.value == 0
|
|
||||||
assert acc.char_on_secondary.value == 1
|
|
||||||
hk_driver.add_accessory(acc)
|
hk_driver.add_accessory(acc)
|
||||||
|
|
||||||
|
assert acc.char_color_temp.value == 190
|
||||||
|
assert acc.char_hue.value == 27
|
||||||
|
assert acc.char_saturation.value == 16
|
||||||
|
|
||||||
|
assert hasattr(acc, "char_color_temp")
|
||||||
|
|
||||||
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_COLOR_TEMP: 224})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await acc.run()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert acc.char_color_temp.value == 224
|
||||||
|
assert acc.char_hue.value == 27
|
||||||
|
assert acc.char_saturation.value == 27
|
||||||
|
|
||||||
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_COLOR_TEMP: 352})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await acc.run()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert acc.char_color_temp.value == 352
|
||||||
|
assert acc.char_hue.value == 28
|
||||||
|
assert acc.char_saturation.value == 61
|
||||||
|
|
||||||
|
char_on_iid = acc.char_on.to_HAP()[HAP_REPR_IID]
|
||||||
|
char_brightness_iid = acc.char_brightness.to_HAP()[HAP_REPR_IID]
|
||||||
char_hue_iid = acc.char_hue.to_HAP()[HAP_REPR_IID]
|
char_hue_iid = acc.char_hue.to_HAP()[HAP_REPR_IID]
|
||||||
char_saturation_iid = acc.char_saturation.to_HAP()[HAP_REPR_IID]
|
char_saturation_iid = acc.char_saturation.to_HAP()[HAP_REPR_IID]
|
||||||
char_color_temperature_iid = acc.char_color_temperature.to_HAP()[HAP_REPR_IID]
|
char_color_temp_iid = acc.char_color_temp.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
|
# Set from HomeKit
|
||||||
|
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
||||||
|
|
||||||
|
hk_driver.set_characteristics(
|
||||||
|
{
|
||||||
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
|
HAP_REPR_VALUE: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_color_temp_iid,
|
||||||
|
HAP_REPR_VALUE: 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_hue_iid,
|
||||||
|
HAP_REPR_VALUE: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_saturation_iid,
|
||||||
|
HAP_REPR_VALUE: 50,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mock_addr",
|
||||||
|
)
|
||||||
|
await _wait_for_light_coalesce(hass)
|
||||||
|
assert call_turn_on[0]
|
||||||
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
|
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
||||||
|
assert call_turn_on[0].data[ATTR_COLOR_TEMP] == 250
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
assert (
|
||||||
|
events[-1].data[ATTR_VALUE]
|
||||||
|
== f"Set state to 1, brightness at 20{PERCENTAGE}, color temperature at 250"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only set Hue
|
||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_hue_iid,
|
HAP_REPR_IID: char_hue_iid,
|
||||||
HAP_REPR_VALUE: 145,
|
HAP_REPR_VALUE: 30,
|
||||||
},
|
}
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_saturation_iid,
|
|
||||||
HAP_REPR_VALUE: 75,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
assert acc.char_hue.value == 145
|
await _wait_for_light_coalesce(hass)
|
||||||
assert acc.char_saturation.value == 75
|
assert call_turn_on[1]
|
||||||
|
assert call_turn_on[1].data[ATTR_HS_COLOR] == (30, 50)
|
||||||
|
|
||||||
|
assert events[-1].data[ATTR_VALUE] == "set color at (30, 50)"
|
||||||
|
|
||||||
|
# Only set Saturation
|
||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_color_temperature_iid,
|
HAP_REPR_IID: char_saturation_iid,
|
||||||
HAP_REPR_VALUE: 200,
|
HAP_REPR_VALUE: 20,
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
assert acc.char_color_temperature.value == 200
|
await _wait_for_light_coalesce(hass)
|
||||||
|
assert call_turn_on[2]
|
||||||
|
assert call_turn_on[2].data[ATTR_HS_COLOR] == (30, 20)
|
||||||
|
|
||||||
|
assert events[-1].data[ATTR_VALUE] == "set color at (30, 20)"
|
||||||
|
|
||||||
|
# Generate a conflict by setting hue and then color temp
|
||||||
|
hk_driver.set_characteristics(
|
||||||
|
{
|
||||||
|
HAP_REPR_CHARS: [
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_hue_iid,
|
||||||
|
HAP_REPR_VALUE: 80,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mock_addr",
|
||||||
|
)
|
||||||
|
hk_driver.set_characteristics(
|
||||||
|
{
|
||||||
|
HAP_REPR_CHARS: [
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_color_temp_iid,
|
||||||
|
HAP_REPR_VALUE: 320,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mock_addr",
|
||||||
|
)
|
||||||
|
await _wait_for_light_coalesce(hass)
|
||||||
|
assert call_turn_on[3]
|
||||||
|
assert call_turn_on[3].data[ATTR_COLOR_TEMP] == 320
|
||||||
|
assert events[-1].data[ATTR_VALUE] == "color temperature at 320"
|
||||||
|
|
||||||
|
# Generate a conflict by setting color temp then saturation
|
||||||
|
hk_driver.set_characteristics(
|
||||||
|
{
|
||||||
|
HAP_REPR_CHARS: [
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_color_temp_iid,
|
||||||
|
HAP_REPR_VALUE: 404,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mock_addr",
|
||||||
|
)
|
||||||
|
hk_driver.set_characteristics(
|
||||||
|
{
|
||||||
|
HAP_REPR_CHARS: [
|
||||||
|
{
|
||||||
|
HAP_REPR_AID: acc.aid,
|
||||||
|
HAP_REPR_IID: char_saturation_iid,
|
||||||
|
HAP_REPR_VALUE: 35,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mock_addr",
|
||||||
|
)
|
||||||
|
await _wait_for_light_coalesce(hass)
|
||||||
|
assert call_turn_on[4]
|
||||||
|
assert call_turn_on[4].data[ATTR_HS_COLOR] == (80, 35)
|
||||||
|
assert events[-1].data[ATTR_VALUE] == "set color at (80, 35)"
|
||||||
|
|
||||||
|
# Set from HASS
|
||||||
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_HS_COLOR: (100, 100)})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await acc.run()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert acc.char_color_temp.value == 404
|
||||||
|
assert acc.char_hue.value == 100
|
||||||
|
assert acc.char_saturation.value == 100
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("supported_color_modes", [["hs"], ["rgb"], ["xy"]])
|
@pytest.mark.parametrize("supported_color_modes", [["hs"], ["rgb"], ["xy"]])
|
||||||
@ -444,7 +522,7 @@ async def test_light_rgb_color(hass, hk_driver, events, supported_color_modes):
|
|||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on
|
assert call_turn_on
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[0].data[ATTR_HS_COLOR] == (145, 75)
|
assert call_turn_on[0].data[ATTR_HS_COLOR] == (145, 75)
|
||||||
@ -476,13 +554,13 @@ async def test_light_restore(hass, hk_driver, events):
|
|||||||
hk_driver.add_accessory(acc)
|
hk_driver.add_accessory(acc)
|
||||||
|
|
||||||
assert acc.category == 5 # Lightbulb
|
assert acc.category == 5 # Lightbulb
|
||||||
assert acc.chars_primary == []
|
assert acc.chars == []
|
||||||
assert acc.char_on_primary.value == 0
|
assert acc.char_on.value == 0
|
||||||
|
|
||||||
acc = Light(hass, hk_driver, "Light", "light.all_info_set", 2, None)
|
acc = Light(hass, hk_driver, "Light", "light.all_info_set", 2, None)
|
||||||
assert acc.category == 5 # Lightbulb
|
assert acc.category == 5 # Lightbulb
|
||||||
assert acc.chars_primary == ["Brightness"]
|
assert acc.chars == ["Brightness"]
|
||||||
assert acc.char_on_primary.value == 0
|
assert acc.char_on.value == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_light_set_brightness_and_color(hass, hk_driver, events):
|
async def test_light_set_brightness_and_color(hass, hk_driver, events):
|
||||||
@ -503,19 +581,19 @@ async def test_light_set_brightness_and_color(hass, hk_driver, events):
|
|||||||
|
|
||||||
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
||||||
# brightness to 100 when turning on a light on a freshly booted up server.
|
# brightness to 100 when turning on a light on a freshly booted up server.
|
||||||
assert acc.char_brightness_primary.value != 0
|
assert acc.char_brightness.value != 0
|
||||||
char_on_primary_iid = acc.char_on_primary.to_HAP()[HAP_REPR_IID]
|
char_on_iid = acc.char_on.to_HAP()[HAP_REPR_IID]
|
||||||
char_brightness_primary_iid = acc.char_brightness_primary.to_HAP()[HAP_REPR_IID]
|
char_brightness_iid = acc.char_brightness.to_HAP()[HAP_REPR_IID]
|
||||||
char_hue_iid = acc.char_hue.to_HAP()[HAP_REPR_IID]
|
char_hue_iid = acc.char_hue.to_HAP()[HAP_REPR_IID]
|
||||||
char_saturation_iid = acc.char_saturation.to_HAP()[HAP_REPR_IID]
|
char_saturation_iid = acc.char_saturation.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
await acc.run()
|
await acc.run()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 100
|
assert acc.char_brightness.value == 100
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 40
|
assert acc.char_brightness.value == 40
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_HS_COLOR: (4.5, 9.2)})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_HS_COLOR: (4.5, 9.2)})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -528,14 +606,10 @@ async def test_light_set_brightness_and_color(hass, hk_driver, events):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_brightness_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 20,
|
HAP_REPR_VALUE: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -552,7 +626,7 @@ async def test_light_set_brightness_and_color(hass, hk_driver, events):
|
|||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on[0]
|
assert call_turn_on[0]
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
||||||
@ -583,22 +657,22 @@ async def test_light_set_brightness_and_color_temp(hass, hk_driver, events):
|
|||||||
|
|
||||||
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
# Initial value can be anything but 0. If it is 0, it might cause HomeKit to set the
|
||||||
# brightness to 100 when turning on a light on a freshly booted up server.
|
# brightness to 100 when turning on a light on a freshly booted up server.
|
||||||
assert acc.char_brightness_primary.value != 0
|
assert acc.char_brightness.value != 0
|
||||||
char_on_primary_iid = acc.char_on_primary.to_HAP()[HAP_REPR_IID]
|
char_on_iid = acc.char_on.to_HAP()[HAP_REPR_IID]
|
||||||
char_brightness_primary_iid = acc.char_brightness_primary.to_HAP()[HAP_REPR_IID]
|
char_brightness_iid = acc.char_brightness.to_HAP()[HAP_REPR_IID]
|
||||||
char_color_temperature_iid = acc.char_color_temperature.to_HAP()[HAP_REPR_IID]
|
char_color_temp_iid = acc.char_color_temp.to_HAP()[HAP_REPR_IID]
|
||||||
|
|
||||||
await acc.run()
|
await acc.run()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 100
|
assert acc.char_brightness.value == 100
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_BRIGHTNESS: 102})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_brightness_primary.value == 40
|
assert acc.char_brightness.value == 40
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_COLOR_TEMP: (224.14)})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_COLOR_TEMP: (224.14)})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.char_color_temperature.value == 224
|
assert acc.char_color_temp.value == 224
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
call_turn_on = async_mock_service(hass, DOMAIN, "turn_on")
|
||||||
@ -606,26 +680,22 @@ async def test_light_set_brightness_and_color_temp(hass, hk_driver, events):
|
|||||||
hk_driver.set_characteristics(
|
hk_driver.set_characteristics(
|
||||||
{
|
{
|
||||||
HAP_REPR_CHARS: [
|
HAP_REPR_CHARS: [
|
||||||
|
{HAP_REPR_AID: acc.aid, HAP_REPR_IID: char_on_iid, HAP_REPR_VALUE: 1},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_on_primary_iid,
|
HAP_REPR_IID: char_brightness_iid,
|
||||||
HAP_REPR_VALUE: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
HAP_REPR_AID: acc.aid,
|
|
||||||
HAP_REPR_IID: char_brightness_primary_iid,
|
|
||||||
HAP_REPR_VALUE: 20,
|
HAP_REPR_VALUE: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
HAP_REPR_AID: acc.aid,
|
HAP_REPR_AID: acc.aid,
|
||||||
HAP_REPR_IID: char_color_temperature_iid,
|
HAP_REPR_IID: char_color_temp_iid,
|
||||||
HAP_REPR_VALUE: 250,
|
HAP_REPR_VALUE: 250,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mock_addr",
|
"mock_addr",
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await _wait_for_light_coalesce(hass)
|
||||||
assert call_turn_on[0]
|
assert call_turn_on[0]
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20
|
||||||
|
Loading…
x
Reference in New Issue
Block a user