Skip to content
Draft
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
5 changes: 2 additions & 3 deletions tests/data/devices/ericsity-gl-c-009p-0x25013001.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,16 +386,15 @@
"on": false,
"brightness": 254,
"xy_color": null,
"color_temp": 500,
"color_temp": null,
"effect_list": [
"off"
],
"effect": "off",
"supported_features": 40,
"color_mode": "color_temp",
"color_mode": "brightness",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff"
],
"off_with_transition": false,
Expand Down
5 changes: 2 additions & 3 deletions tests/data/devices/gledopto-gl-c-009p-0x17013001.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,15 @@
"on": false,
"brightness": 143,
"xy_color": null,
"color_temp": 158,
"color_temp": null,
"effect_list": [
"off"
],
"effect": "off",
"supported_features": 40,
"color_mode": "color_temp",
"color_mode": "brightness",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff"
],
"off_with_transition": false,
Expand Down
5 changes: 2 additions & 3 deletions tests/data/devices/gledopto-gl-sd-301p-0x26013001.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,15 @@
"on": true,
"brightness": 254,
"xy_color": null,
"color_temp": 495,
"color_temp": null,
"effect_list": [
"off"
],
"effect": "off",
"supported_features": 40,
"color_mode": "color_temp",
"color_mode": "brightness",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff"
],
"off_with_transition": false,
Expand Down
3 changes: 1 addition & 2 deletions tests/data/devices/homr-hrmsn01.json
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@
0.0,
0.0
],
"color_temp": 0,
"color_temp": null,
"effect_list": [
"off"
],
Expand All @@ -517,7 +517,6 @@
"color_mode": "xy",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff",
"xy"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,16 +365,15 @@
0.16398870832379644,
0.13199053940642405
],
"color_temp": 1901,
"color_temp": null,
"effect_list": [
"off"
],
"effect": "off",
"supported_features": 40,
"color_mode": "color_temp",
"color_mode": "xy",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff",
"xy"
],
Expand Down
5 changes: 2 additions & 3 deletions tests/data/devices/sonoff-dongle-e-r.json
Original file line number Diff line number Diff line change
Expand Up @@ -500,16 +500,15 @@
"on": false,
"brightness": 51,
"xy_color": null,
"color_temp": 17476,
"color_temp": null,
"effect_list": [
"off"
],
"effect": "off",
"supported_features": 40,
"color_mode": "color_temp",
"color_mode": "brightness",
"supported_color_modes": [
"brightness",
"color_temp",
"onoff"
],
"off_with_transition": false,
Expand Down
183 changes: 182 additions & 1 deletion tests/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from unittest.mock import AsyncMock, call, patch, sentinel

import pytest
from zigpy.profiles import zha
from zigpy.profiles import zha, zll
from zigpy.zcl.clusters import general, lighting
import zigpy.zcl.foundation as zcl_f
import zigpy.zdo.types as zdo_t
Expand Down Expand Up @@ -2248,6 +2248,187 @@ async def test_poll_updates_color_mode_on_dual_mode_light(
assert entity.state["color_temp"] is None


_ZHA_COLOR = (zha.PROFILE_ID, zha.DeviceType.COLOR_DIMMABLE_LIGHT)
_ZHA_CT = (zha.PROFILE_ID, zha.DeviceType.COLOR_TEMPERATURE_LIGHT)
_ZHA_EXTENDED = (zha.PROFILE_ID, zha.DeviceType.EXTENDED_COLOR_LIGHT)
_ZLL_CT = (zll.PROFILE_ID, zll.DeviceType.COLOR_TEMPERATURE_LIGHT)
_ZLL_EXTENDED = (zll.PROFILE_ID, zll.DeviceType.EXTENDED_COLOR_LIGHT)
_ZCL_CT: lighting.ColorMode = lighting.ColorMode.Color_temperature


def _create_color_light(
zha_gateway: Gateway,
*,
endpoint: tuple[int, int] = _ZHA_COLOR,
color_attributes: dict[str, Any] | None = None,
) -> tuple[Any, Any]:
profile_id, device_type = endpoint
zigpy_device = create_mock_zigpy_device(
zha_gateway,
{
1: {
**LIGHT_COLOR[1],
SIG_EP_PROFILE: profile_id,
SIG_EP_TYPE: device_type,
}
},
)
color_cluster = zigpy_device.endpoints[1].light_color
color_cluster.PLUGGED_ATTR_READS = color_attributes or {}
update_attribute_cache(color_cluster)
return zigpy_device, color_cluster


async def test_explicit_negative_color_capability_suppresses_color_temperature(
zha_gateway: Gateway,
) -> None:
"""Test an explicit bitmap without CT overrides fallback signals."""
zigpy_device, color_cluster = _create_color_light(
zha_gateway,
endpoint=_ZHA_EXTENDED,
color_attributes={
"color_capabilities": lighting.ColorCapabilities.XY_attributes,
"color_mode": _ZCL_CT,
"color_temperature": 250,
},
)
level_cluster = zigpy_device.endpoints[1].level
zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
entity = get_entity(zha_device, platform=Platform.LIGHT)

assert entity.supported_color_modes == {ColorMode.XY}
assert entity.state["color_temp"] is None

level_cluster.PLUGGED_ATTR_READS = {"current_level": 180}
color_cluster.PLUGGED_ATTR_READS = {
"color_mode": lighting.ColorMode.X_and_Y,
"color_temperature": 275,
"current_x": 30000,
"current_y": 25000,
}
color_cluster.read_attributes.reset_mock()
await entity.async_update()

polled_attributes = color_cluster.read_attributes.await_args.args[0]
assert "color_temperature" not in polled_attributes
assert entity.state["color_mode"] == ColorMode.XY
assert entity.state["xy_color"] == (30000 / 65535, 25000 / 65535)
assert entity.state["brightness"] == 180


async def test_recompute_capabilities_clears_unsupported_color_temperature(
zha_gateway: Gateway,
) -> None:
"""Test recomputing capabilities clears stale color temperature state."""
zigpy_device, color_cluster = _create_color_light(
zha_gateway,
color_attributes={
"color_mode": _ZCL_CT,
"color_temperature": 250,
},
)
zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
entity = get_entity(zha_device, platform=Platform.LIGHT)

assert entity.supported_color_modes == {ColorMode.COLOR_TEMP, ColorMode.XY}
assert entity.state["color_temp"] == 250

color_cluster._attr_cache.set_value(
lighting.Color.AttributeDefs.color_capabilities,
lighting.ColorCapabilities.XY_attributes,
)
entity.recompute_capabilities()

assert entity.supported_color_modes == {ColorMode.XY}
assert entity.state["color_temp"] is None


@pytest.mark.parametrize(
(
"capability_state",
"endpoint",
"color_attributes",
"expected_color_temp_support",
),
(
pytest.param("none", _ZHA_COLOR, {"color_mode": _ZCL_CT}, True, id="none"),
pytest.param(
"unsupported",
_ZHA_COLOR,
{"color_mode": _ZCL_CT},
True,
id="unsupported",
),
pytest.param("absent", _ZLL_CT, {}, True, id="zll-color-temperature"),
pytest.param("absent", _ZLL_EXTENDED, {}, True, id="zll-extended"),
pytest.param(
"absent",
_ZHA_COLOR,
{"color_temperature": 250},
False,
id="stale-temperature",
),
),
)
async def test_color_temperature_capability_evidence(
zha_gateway: Gateway,
capability_state: str,
endpoint: tuple[int, int],
color_attributes: dict[str, Any],
expected_color_temp_support: bool,
) -> None:
"""Test the authoritative signals for color temperature support."""
zigpy_device, color_cluster = _create_color_light(
zha_gateway,
endpoint=endpoint,
color_attributes=color_attributes,
)

capability_attr = lighting.Color.AttributeDefs.color_capabilities
if capability_state == "none":
color_cluster._attr_cache.set_value(capability_attr, None)
elif capability_state == "unsupported":
color_cluster._attr_cache.set_value(
capability_attr, lighting.ColorCapabilities.XY_attributes
)
color_cluster.add_unsupported_attribute(capability_attr)

zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
entity = get_entity(zha_device, platform=Platform.LIGHT)

assert (
ColorMode.COLOR_TEMP in entity.supported_color_modes
) is expected_color_temp_support
assert ColorMode.XY in entity.supported_color_modes
assert "colorloop" not in entity.state["effect_list"]
if not expected_color_temp_support:
assert entity.state["color_temp"] is None


async def test_missing_color_attribute_definitions(zha_gateway: Gateway) -> None:
"""Test a partial color cluster remains usable."""
zigpy_device, color_cluster = _create_color_light(
zha_gateway,
endpoint=_ZHA_CT,
)
missing_attributes = {"color_capabilities", "color_mode", "color_temperature"}
color_cluster.attributes_by_name = {
name: attribute
for name, attribute in color_cluster.attributes_by_name.items()
if name not in missing_attributes
}

zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
entity = get_entity(zha_device, platform=Platform.LIGHT)
assert entity.supported_color_modes == {ColorMode.COLOR_TEMP, ColorMode.XY}

color_cluster.read_attributes.reset_mock()
await entity.async_update()

polled_attributes = color_cluster.read_attributes.await_args.args[0]
assert set(polled_attributes) == {"current_x", "current_y"}


async def test_turn_on_cancellation_cleans_up_transition_flag(
zha_gateway: Gateway,
) -> None:
Expand Down
Loading
Loading