Skip to content

Commit ebb2aa9

Browse files
committed
Fix alert flash RGB payload
Normalize alert flash colors to RGB lists so Home Assistant service validation accepts internally triggered visual alerts. Add regression coverage for the flash payload shape and document the fix in the changelog.
1 parent add0313 commit ebb2aa9

4 files changed

Lines changed: 24 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This project follows a practical changelog format for Home Assistant and HACS us
66

77
## Unreleased
88

9+
- Fixed alert flash color payloads so internally triggered visual alerts send RGB lists accepted by Home Assistant service validation.
910
- Added user-friendly headers to V2 card YAML exports with Manual-card paste instructions, `dump_cards` refresh guidance, and frontend dependency reminders.
1011
- Fixed alert flash payloads so optional visual-indicator power entities are omitted when unset, avoiding schema errors and repeated debug logs.
1112
- Refined Air Control UI chips so AQ and humidity rows use House, Upstairs, Downstairs ordering and removed the Kitchen temperature-slope chip from the humidity row.

automations/engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ async def _handle_alerts(self) -> Tuple[bool, List[Dict[str, Any]]]:
492492
try:
493493
flash_payload = {
494494
"lights": lights,
495-
"color": (255, 0, 0) if alert.get("flash_mode") == "red" else (255, 255, 255),
495+
"color": [255, 0, 0] if alert.get("flash_mode") == "red" else [255, 255, 255],
496496
"duration": alert.get("duration", 10),
497497
}
498498
power_entity = alert.get("power_entity")

services.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,25 @@ def _validate_dashboard_url_path(value: str) -> str:
106106
return text
107107

108108

109+
def _validate_rgb_color(value) -> List[int]:
110+
"""Accept service/YAML RGB colors and normalize them to a plain list."""
111+
if isinstance(value, tuple):
112+
values = list(value)
113+
else:
114+
values = cv.ensure_list(value)
115+
values = [vol.Coerce(int)(item) for item in values]
116+
if len(values) < 3:
117+
raise vol.Invalid("RGB color must include red, green, and blue values")
118+
for item in values[:3]:
119+
if item < 0 or item > 255:
120+
raise vol.Invalid("RGB color values must be between 0 and 255")
121+
return values[:3]
122+
123+
109124
SERVICE_FLASH_SCHEMA = vol.Schema({
110125
vol.Optional("power_entity"): cv.entity_id,
111126
vol.Optional("lights", default=[]): cv.entity_ids,
112-
vol.Optional("color", default=(255, 0, 0)): vol.All(cv.ensure_list, [vol.Coerce(int)]),
127+
vol.Optional("color", default=[255, 0, 0]): _validate_rgb_color,
113128
vol.Optional("duration", default=10): vol.All(vol.Coerce(int), vol.Range(min=1, max=300)),
114129
vol.Optional("flash_count", default=None): vol.Any(
115130
None,

tests 2/test_runtime_card_sanity.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,12 @@ async def _run_runtime_assertions(engine_mod) -> None:
825825
]
826826
assert dynamic_flash_calls
827827
assert all("power_entity" not in data for data in dynamic_flash_calls)
828+
assert all(isinstance(data.get("color"), list) for data in dynamic_flash_calls)
829+
assert all(len(data.get("color")) == 3 for data in dynamic_flash_calls)
830+
assert all(
831+
all(isinstance(channel, int) for channel in data.get("color"))
832+
for data in dynamic_flash_calls
833+
)
828834

829835
# Alerts without target lights should still activate runtime alert mode,
830836
# but skip light flash service calls cleanly.

0 commit comments

Comments
 (0)