Add flash light device actions (#33689)

* Add flash light device actions

* Single action with extra fields

* Change extra_fields to dictionary
This commit is contained in:
Robert Chmielowiec 2020-04-13 19:30:20 +02:00 committed by GitHub
parent 2239f2f732
commit 7bd6f5413d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 145 additions and 24 deletions

View File

@ -87,6 +87,7 @@ VALID_BRIGHTNESS = vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255))
VALID_BRIGHTNESS_PCT = vol.All(vol.Coerce(float), vol.Range(min=0, max=100))
VALID_BRIGHTNESS_STEP = vol.All(vol.Coerce(int), vol.Clamp(min=-255, max=255))
VALID_BRIGHTNESS_STEP_PCT = vol.All(vol.Coerce(float), vol.Clamp(min=-100, max=100))
VALID_FLASH = vol.In([FLASH_SHORT, FLASH_LONG])
LIGHT_TURN_ON_SCHEMA = {
vol.Exclusive(ATTR_PROFILE, COLOR_GROUP): cv.string,
@ -116,7 +117,7 @@ LIGHT_TURN_ON_SCHEMA = {
),
vol.Exclusive(ATTR_KELVIN, COLOR_GROUP): vol.All(vol.Coerce(int), vol.Range(min=0)),
ATTR_WHITE_VALUE: vol.All(vol.Coerce(int), vol.Range(min=0, max=255)),
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
ATTR_FLASH: VALID_FLASH,
ATTR_EFFECT: cv.string,
}
@ -252,10 +253,7 @@ async def async_setup(hass, config):
component.async_register_entity_service(
SERVICE_TURN_OFF,
{
ATTR_TRANSITION: VALID_TRANSITION,
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
},
{ATTR_TRANSITION: VALID_TRANSITION, ATTR_FLASH: VALID_FLASH},
"async_turn_off",
)

View File

@ -4,6 +4,13 @@ from typing import List
import voluptuous as vol
from homeassistant.components.device_automation import toggle_entity
from homeassistant.components.light import (
ATTR_FLASH,
FLASH_SHORT,
SUPPORT_FLASH,
VALID_BRIGHTNESS_PCT,
VALID_FLASH,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
@ -19,6 +26,7 @@ from . import ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRI
TYPE_BRIGHTNESS_INCREASE = "brightness_increase"
TYPE_BRIGHTNESS_DECREASE = "brightness_decrease"
TYPE_FLASH = "flash"
ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
@ -26,11 +34,10 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
vol.Required(CONF_DOMAIN): DOMAIN,
vol.Required(CONF_TYPE): vol.In(
toggle_entity.DEVICE_ACTION_TYPES
+ [TYPE_BRIGHTNESS_INCREASE, TYPE_BRIGHTNESS_DECREASE]
),
vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
+ [TYPE_BRIGHTNESS_INCREASE, TYPE_BRIGHTNESS_DECREASE, TYPE_FLASH]
),
vol.Optional(ATTR_BRIGHTNESS_PCT): VALID_BRIGHTNESS_PCT,
vol.Optional(ATTR_FLASH): VALID_FLASH,
}
)
@ -60,6 +67,12 @@ async def async_call_action_from_config(
elif ATTR_BRIGHTNESS_PCT in config:
data[ATTR_BRIGHTNESS_PCT] = config[ATTR_BRIGHTNESS_PCT]
if config[CONF_TYPE] == TYPE_FLASH:
if ATTR_FLASH in config:
data[ATTR_FLASH] = config[ATTR_FLASH]
else:
data[ATTR_FLASH] = FLASH_SHORT
await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=context
)
@ -100,6 +113,18 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> List[dict]:
)
)
if supported_features & SUPPORT_FLASH:
actions.extend(
(
{
CONF_TYPE: TYPE_FLASH,
"device_id": device_id,
"entity_id": entry.entity_id,
"domain": DOMAIN,
},
)
)
return actions
@ -119,15 +144,12 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di
elif entry:
supported_features = entry.supported_features
if not supported_features & SUPPORT_BRIGHTNESS:
return {}
extra_fields = {}
return {
"extra_fields": vol.Schema(
{
vol.Optional(ATTR_BRIGHTNESS_PCT): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
)
}
)
}
if supported_features & SUPPORT_BRIGHTNESS:
extra_fields[vol.Optional(ATTR_BRIGHTNESS_PCT)] = VALID_BRIGHTNESS_PCT
if supported_features & SUPPORT_FLASH:
extra_fields[vol.Optional(ATTR_FLASH)] = VALID_FLASH
return {"extra_fields": vol.Schema(extra_fields)} if extra_fields else {}

View File

@ -5,7 +5,8 @@
"brightness_increase": "Increase {entity_name} brightness",
"toggle": "Toggle {entity_name}",
"turn_on": "Turn on {entity_name}",
"turn_off": "Turn off {entity_name}"
"turn_off": "Turn off {entity_name}",
"flash": "Flash {entity_name}"
},
"condition_type": {
"is_on": "{entity_name} is on",

View File

@ -2,7 +2,13 @@
import pytest
import homeassistant.components.automation as automation
from homeassistant.components.light import DOMAIN, SUPPORT_BRIGHTNESS
from homeassistant.components.light import (
DOMAIN,
FLASH_LONG,
FLASH_SHORT,
SUPPORT_BRIGHTNESS,
SUPPORT_FLASH,
)
from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON
from homeassistant.helpers import device_registry
from homeassistant.setup import async_setup_component
@ -48,7 +54,7 @@ async def test_get_actions(hass, device_reg, entity_reg):
"test",
"5678",
device_id=device_entry.id,
supported_features=SUPPORT_BRIGHTNESS,
supported_features=SUPPORT_BRIGHTNESS | SUPPORT_FLASH,
)
expected_actions = [
{
@ -81,6 +87,12 @@ async def test_get_actions(hass, device_reg, entity_reg):
"device_id": device_entry.id,
"entity_id": f"{DOMAIN}.test_5678",
},
{
"domain": DOMAIN,
"type": "flash",
"device_id": device_entry.id,
"entity_id": f"{DOMAIN}.test_5678",
},
]
actions = await async_get_device_automations(hass, "action", device_entry.id)
assert actions == expected_actions
@ -128,7 +140,7 @@ async def test_get_action_capabilities_brightness(hass, device_reg, entity_reg):
{
"name": "brightness_pct",
"optional": True,
"type": "integer",
"type": "float",
"valueMax": 100,
"valueMin": 0,
}
@ -146,6 +158,45 @@ async def test_get_action_capabilities_brightness(hass, device_reg, entity_reg):
assert capabilities == {"extra_fields": []}
async def test_get_action_capabilities_flash(hass, device_reg, entity_reg):
"""Test we get the expected capabilities from a light action."""
config_entry = MockConfigEntry(domain="test", data={})
config_entry.add_to_hass(hass)
device_entry = device_reg.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
entity_reg.async_get_or_create(
DOMAIN,
"test",
"5678",
device_id=device_entry.id,
supported_features=SUPPORT_FLASH,
)
expected_capabilities = {
"extra_fields": [
{
"name": "flash",
"optional": True,
"type": "select",
"options": [("short", "short"), ("long", "long")],
}
]
}
actions = await async_get_device_automations(hass, "action", device_entry.id)
assert len(actions) == 4
for action in actions:
capabilities = await async_get_device_automation_capabilities(
hass, "action", action
)
if action["type"] == "turn_on":
assert capabilities == expected_capabilities
else:
assert capabilities == {"extra_fields": []}
async def test_action(hass, calls):
"""Test for turn_on and turn_off actions."""
platform = getattr(hass.components, f"test.{DOMAIN}")
@ -187,6 +238,25 @@ async def test_action(hass, calls):
"type": "toggle",
},
},
{
"trigger": {"platform": "event", "event_type": "test_flash_short"},
"action": {
"domain": DOMAIN,
"device_id": "",
"entity_id": ent1.entity_id,
"type": "flash",
},
},
{
"trigger": {"platform": "event", "event_type": "test_flash_long"},
"action": {
"domain": DOMAIN,
"device_id": "",
"entity_id": ent1.entity_id,
"type": "flash",
"flash": "long",
},
},
{
"trigger": {
"platform": "event",
@ -252,6 +322,22 @@ async def test_action(hass, calls):
await hass.async_block_till_done()
assert hass.states.get(ent1.entity_id).state == STATE_ON
hass.bus.async_fire("test_toggle")
await hass.async_block_till_done()
assert hass.states.get(ent1.entity_id).state == STATE_OFF
hass.bus.async_fire("test_flash_short")
await hass.async_block_till_done()
assert hass.states.get(ent1.entity_id).state == STATE_ON
hass.bus.async_fire("test_toggle")
await hass.async_block_till_done()
assert hass.states.get(ent1.entity_id).state == STATE_OFF
hass.bus.async_fire("test_flash_long")
await hass.async_block_till_done()
assert hass.states.get(ent1.entity_id).state == STATE_ON
turn_on_calls = async_mock_service(hass, DOMAIN, "turn_on")
hass.bus.async_fire("test_brightness_increase")
@ -281,3 +367,17 @@ async def test_action(hass, calls):
assert len(turn_on_calls) == 4
assert turn_on_calls[3].data["entity_id"] == ent1.entity_id
assert "brightness_pct" not in turn_on_calls[3].data
hass.bus.async_fire("test_flash_short")
await hass.async_block_till_done()
assert len(turn_on_calls) == 5
assert turn_on_calls[4].data["entity_id"] == ent1.entity_id
assert turn_on_calls[4].data["flash"] == FLASH_SHORT
hass.bus.async_fire("test_flash_long")
await hass.async_block_till_done()
assert len(turn_on_calls) == 6
assert turn_on_calls[5].data["entity_id"] == ent1.entity_id
assert turn_on_calls[5].data["flash"] == FLASH_LONG