Add button to homeworks (#112269)
This commit is contained in:
parent
8e2de51710
commit
a515603aaf
9 changed files with 763 additions and 9 deletions
|
@ -546,6 +546,7 @@ omit =
|
|||
homeassistant/components/homematic/sensor.py
|
||||
homeassistant/components/homematic/switch.py
|
||||
homeassistant/components/homeworks/__init__.py
|
||||
homeassistant/components/homeworks/button.py
|
||||
homeassistant/components/homeworks/light.py
|
||||
homeassistant/components/horizon/media_player.py
|
||||
homeassistant/components/hp_ilo/sensor.py
|
||||
|
|
|
@ -36,7 +36,7 @@ from .const import (
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.LIGHT]
|
||||
PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.LIGHT]
|
||||
|
||||
EVENT_BUTTON_PRESS = "homeworks_button_press"
|
||||
EVENT_BUTTON_RELEASE = "homeworks_button_release"
|
||||
|
@ -186,6 +186,7 @@ class HomeworksEntity(Entity):
|
|||
self._controller_id, self._addr, self._idx
|
||||
)
|
||||
self._controller = controller
|
||||
self._attr_extra_state_attributes = {"homeworks_address": self._addr}
|
||||
|
||||
|
||||
class HomeworksKeypad:
|
||||
|
|
78
homeassistant/components/homeworks/button.py
Normal file
78
homeassistant/components/homeworks/button.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
"""Support for Lutron Homeworks buttons."""
|
||||
from __future__ import annotations
|
||||
|
||||
from time import sleep
|
||||
|
||||
from pyhomeworks.pyhomeworks import Homeworks
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import HomeworksData, HomeworksEntity
|
||||
from .const import (
|
||||
CONF_ADDR,
|
||||
CONF_BUTTONS,
|
||||
CONF_CONTROLLER_ID,
|
||||
CONF_KEYPADS,
|
||||
CONF_NUMBER,
|
||||
CONF_RELEASE_DELAY,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Homeworks buttons."""
|
||||
data: HomeworksData = hass.data[DOMAIN][entry.entry_id]
|
||||
controller = data.controller
|
||||
controller_id = entry.options[CONF_CONTROLLER_ID]
|
||||
devs = []
|
||||
for keypad in entry.options.get(CONF_KEYPADS, []):
|
||||
for button in keypad[CONF_BUTTONS]:
|
||||
dev = HomeworksButton(
|
||||
controller,
|
||||
controller_id,
|
||||
keypad[CONF_ADDR],
|
||||
keypad[CONF_NAME],
|
||||
button[CONF_NAME],
|
||||
button[CONF_NUMBER],
|
||||
button[CONF_RELEASE_DELAY],
|
||||
)
|
||||
devs.append(dev)
|
||||
async_add_entities(devs, True)
|
||||
|
||||
|
||||
class HomeworksButton(HomeworksEntity, ButtonEntity):
|
||||
"""Homeworks Button."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
controller: Homeworks,
|
||||
controller_id: str,
|
||||
addr: str,
|
||||
keypad_name: str,
|
||||
button_name: str,
|
||||
button_number: int,
|
||||
release_delay: float,
|
||||
) -> None:
|
||||
"""Create device with Addr, name, and rate."""
|
||||
super().__init__(controller, controller_id, addr, button_number, button_name)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{controller_id}.{addr}")}, name=keypad_name
|
||||
)
|
||||
self._release_delay = release_delay
|
||||
|
||||
def press(self) -> None:
|
||||
"""Press the button."""
|
||||
# pylint: disable-next=protected-access
|
||||
self._controller._send(f"KBP, {self._addr}, {self._idx}")
|
||||
if not self._release_delay:
|
||||
return
|
||||
sleep(self._release_delay)
|
||||
# pylint: disable-next=protected-access
|
||||
self._controller._send(f"KBR, {self._addr}, {self._idx}")
|
|
@ -8,6 +8,7 @@ from typing import Any
|
|||
from pyhomeworks.pyhomeworks import Homeworks
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
|
||||
|
@ -37,11 +38,15 @@ from homeassistant.util import slugify
|
|||
from . import DEFAULT_FADE_RATE, calculate_unique_id
|
||||
from .const import (
|
||||
CONF_ADDR,
|
||||
CONF_BUTTONS,
|
||||
CONF_CONTROLLER_ID,
|
||||
CONF_DIMMERS,
|
||||
CONF_INDEX,
|
||||
CONF_KEYPADS,
|
||||
CONF_NUMBER,
|
||||
CONF_RATE,
|
||||
CONF_RELEASE_DELAY,
|
||||
DEFAULT_BUTTON_NAME,
|
||||
DEFAULT_KEYPAD_NAME,
|
||||
DEFAULT_LIGHT_NAME,
|
||||
DOMAIN,
|
||||
|
@ -71,6 +76,18 @@ LIGHT_EDIT = {
|
|||
),
|
||||
}
|
||||
|
||||
BUTTON_EDIT = {
|
||||
vol.Optional(CONF_RELEASE_DELAY, default=0): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=0,
|
||||
max=5,
|
||||
step=0.01,
|
||||
mode=selector.NumberSelectorMode.BOX,
|
||||
unit_of_measurement="s",
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
validate_addr = cv.matches_regex(r"\[\d\d:\d\d:\d\d:\d\d\]")
|
||||
|
||||
|
@ -160,6 +177,31 @@ def _validate_address(handler: SchemaCommonFlowHandler, addr: str) -> None:
|
|||
raise SchemaFlowError("duplicated_addr")
|
||||
|
||||
|
||||
def _validate_button_number(handler: SchemaCommonFlowHandler, number: int) -> None:
|
||||
"""Validate button number."""
|
||||
keypad = handler.flow_state["_idx"]
|
||||
buttons: list[dict[str, Any]] = handler.options[CONF_KEYPADS][keypad][CONF_BUTTONS]
|
||||
|
||||
for button in buttons:
|
||||
if button[CONF_NUMBER] == number:
|
||||
raise SchemaFlowError("duplicated_number")
|
||||
|
||||
|
||||
async def validate_add_button(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Validate button input."""
|
||||
user_input[CONF_NUMBER] = int(user_input[CONF_NUMBER])
|
||||
_validate_button_number(handler, user_input[CONF_NUMBER])
|
||||
|
||||
# Standard behavior is to merge the result with the options.
|
||||
# In this case, we want to add a sub-item so we update the options directly.
|
||||
keypad = handler.flow_state["_idx"]
|
||||
buttons: list[dict[str, Any]] = handler.options[CONF_KEYPADS][keypad][CONF_BUTTONS]
|
||||
buttons.append(user_input)
|
||||
return {}
|
||||
|
||||
|
||||
async def validate_add_keypad(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
|
@ -169,7 +211,7 @@ async def validate_add_keypad(
|
|||
# Standard behavior is to merge the result with the options.
|
||||
# In this case, we want to add a sub-item so we update the options directly.
|
||||
items = handler.options[CONF_KEYPADS]
|
||||
items.append(user_input)
|
||||
items.append(user_input | {CONF_BUTTONS: []})
|
||||
return {}
|
||||
|
||||
|
||||
|
@ -186,6 +228,37 @@ async def validate_add_light(
|
|||
return {}
|
||||
|
||||
|
||||
async def get_select_button_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||
"""Return schema for selecting a button."""
|
||||
keypad = handler.flow_state["_idx"]
|
||||
buttons: list[dict[str, Any]] = handler.options[CONF_KEYPADS][keypad][CONF_BUTTONS]
|
||||
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_INDEX): vol.In(
|
||||
{
|
||||
str(index): f"{config[CONF_NAME]} ({config[CONF_NUMBER]})"
|
||||
for index, config in enumerate(buttons)
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get_select_keypad_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||
"""Return schema for selecting a keypad."""
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_INDEX): vol.In(
|
||||
{
|
||||
str(index): f"{config[CONF_NAME]} ({config[CONF_ADDR]})"
|
||||
for index, config in enumerate(handler.options[CONF_KEYPADS])
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get_select_light_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||
"""Return schema for selecting a light."""
|
||||
return vol.Schema(
|
||||
|
@ -200,6 +273,14 @@ async def get_select_light_schema(handler: SchemaCommonFlowHandler) -> vol.Schem
|
|||
)
|
||||
|
||||
|
||||
async def validate_select_button(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Store button index in flow state."""
|
||||
handler.flow_state["_button_idx"] = int(user_input[CONF_INDEX])
|
||||
return {}
|
||||
|
||||
|
||||
async def validate_select_keypad_light(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
|
@ -208,6 +289,15 @@ async def validate_select_keypad_light(
|
|||
return {}
|
||||
|
||||
|
||||
async def get_edit_button_suggested_values(
|
||||
handler: SchemaCommonFlowHandler,
|
||||
) -> dict[str, Any]:
|
||||
"""Return suggested values for button editing."""
|
||||
keypad_idx: int = handler.flow_state["_idx"]
|
||||
button_idx: int = handler.flow_state["_button_idx"]
|
||||
return dict(handler.options[CONF_KEYPADS][keypad_idx][CONF_BUTTONS][button_idx])
|
||||
|
||||
|
||||
async def get_edit_light_suggested_values(
|
||||
handler: SchemaCommonFlowHandler,
|
||||
) -> dict[str, Any]:
|
||||
|
@ -216,6 +306,19 @@ async def get_edit_light_suggested_values(
|
|||
return dict(handler.options[CONF_DIMMERS][idx])
|
||||
|
||||
|
||||
async def validate_button_edit(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Update edited keypad or light."""
|
||||
# Standard behavior is to merge the result with the options.
|
||||
# In this case, we want to add a sub-item so we update the options directly.
|
||||
keypad_idx: int = handler.flow_state["_idx"]
|
||||
button_idx: int = handler.flow_state["_button_idx"]
|
||||
buttons: list[dict] = handler.options[CONF_KEYPADS][keypad_idx][CONF_BUTTONS]
|
||||
buttons[button_idx].update(user_input)
|
||||
return {}
|
||||
|
||||
|
||||
async def validate_light_edit(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
|
@ -227,6 +330,22 @@ async def validate_light_edit(
|
|||
return {}
|
||||
|
||||
|
||||
async def get_remove_button_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||
"""Return schema for button removal."""
|
||||
keypad_idx: int = handler.flow_state["_idx"]
|
||||
buttons: list[dict] = handler.options[CONF_KEYPADS][keypad_idx][CONF_BUTTONS]
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_INDEX): cv.multi_select(
|
||||
{
|
||||
str(index): f"{config[CONF_NAME]} ({config[CONF_NUMBER]})"
|
||||
for index, config in enumerate(buttons)
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get_remove_keypad_light_schema(
|
||||
handler: SchemaCommonFlowHandler, *, key: str
|
||||
) -> vol.Schema:
|
||||
|
@ -243,6 +362,37 @@ async def get_remove_keypad_light_schema(
|
|||
)
|
||||
|
||||
|
||||
async def validate_remove_button(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Validate remove keypad or light."""
|
||||
removed_indexes: set[str] = set(user_input[CONF_INDEX])
|
||||
|
||||
# Standard behavior is to merge the result with the options.
|
||||
# In this case, we want to remove sub-items so we update the options directly.
|
||||
entity_registry = er.async_get(handler.parent_handler.hass)
|
||||
keypad_idx: int = handler.flow_state["_idx"]
|
||||
keypad: dict = handler.options[CONF_KEYPADS][keypad_idx]
|
||||
items: list[dict[str, Any]] = []
|
||||
item: dict[str, Any]
|
||||
for index, item in enumerate(keypad[CONF_BUTTONS]):
|
||||
if str(index) not in removed_indexes:
|
||||
items.append(item)
|
||||
button_number = keypad[CONF_BUTTONS][index][CONF_NUMBER]
|
||||
if entity_id := entity_registry.async_get_entity_id(
|
||||
BUTTON_DOMAIN,
|
||||
DOMAIN,
|
||||
calculate_unique_id(
|
||||
handler.options[CONF_CONTROLLER_ID],
|
||||
keypad[CONF_ADDR],
|
||||
button_number,
|
||||
),
|
||||
):
|
||||
entity_registry.async_remove(entity_id)
|
||||
keypad[CONF_BUTTONS] = items
|
||||
return {}
|
||||
|
||||
|
||||
async def validate_remove_keypad_light(
|
||||
handler: SchemaCommonFlowHandler, user_input: dict[str, Any], *, key: str
|
||||
) -> dict[str, Any]:
|
||||
|
@ -292,12 +442,28 @@ DATA_SCHEMA_ADD_KEYPAD = vol.Schema(
|
|||
vol.Required(CONF_ADDR): TextSelector(),
|
||||
}
|
||||
)
|
||||
DATA_SCHEMA_ADD_BUTTON = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_BUTTON_NAME): TextSelector(),
|
||||
vol.Required(CONF_NUMBER): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=1,
|
||||
max=24,
|
||||
step=1,
|
||||
mode=selector.NumberSelectorMode.BOX,
|
||||
),
|
||||
),
|
||||
**BUTTON_EDIT,
|
||||
}
|
||||
)
|
||||
DATA_SCHEMA_EDIT_BUTTON = vol.Schema(BUTTON_EDIT)
|
||||
DATA_SCHEMA_EDIT_LIGHT = vol.Schema(LIGHT_EDIT)
|
||||
|
||||
OPTIONS_FLOW = {
|
||||
"init": SchemaFlowMenuStep(
|
||||
[
|
||||
"add_keypad",
|
||||
"select_edit_keypad",
|
||||
"remove_keypad",
|
||||
"add_light",
|
||||
"select_edit_light",
|
||||
|
@ -309,6 +475,40 @@ OPTIONS_FLOW = {
|
|||
suggested_values=None,
|
||||
validate_user_input=validate_add_keypad,
|
||||
),
|
||||
"select_edit_keypad": SchemaFlowFormStep(
|
||||
get_select_keypad_schema,
|
||||
suggested_values=None,
|
||||
validate_user_input=validate_select_keypad_light,
|
||||
next_step="edit_keypad",
|
||||
),
|
||||
"edit_keypad": SchemaFlowMenuStep(
|
||||
[
|
||||
"add_button",
|
||||
"select_edit_button",
|
||||
"remove_button",
|
||||
]
|
||||
),
|
||||
"add_button": SchemaFlowFormStep(
|
||||
DATA_SCHEMA_ADD_BUTTON,
|
||||
suggested_values=None,
|
||||
validate_user_input=validate_add_button,
|
||||
),
|
||||
"select_edit_button": SchemaFlowFormStep(
|
||||
get_select_button_schema,
|
||||
suggested_values=None,
|
||||
validate_user_input=validate_select_button,
|
||||
next_step="edit_button",
|
||||
),
|
||||
"edit_button": SchemaFlowFormStep(
|
||||
DATA_SCHEMA_EDIT_BUTTON,
|
||||
suggested_values=get_edit_button_suggested_values,
|
||||
validate_user_input=validate_button_edit,
|
||||
),
|
||||
"remove_button": SchemaFlowFormStep(
|
||||
get_remove_button_schema,
|
||||
suggested_values=None,
|
||||
validate_user_input=validate_remove_button,
|
||||
),
|
||||
"remove_keypad": SchemaFlowFormStep(
|
||||
partial(get_remove_keypad_light_schema, key=CONF_KEYPADS),
|
||||
suggested_values=None,
|
||||
|
@ -359,6 +559,14 @@ class HomeworksConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
CONF_KEYPADS: [
|
||||
{
|
||||
CONF_ADDR: keypad[CONF_ADDR],
|
||||
CONF_BUTTONS: [
|
||||
{
|
||||
CONF_NAME: button[CONF_NAME],
|
||||
CONF_NUMBER: button[CONF_NUMBER],
|
||||
CONF_RELEASE_DELAY: button[CONF_RELEASE_DELAY],
|
||||
}
|
||||
for button in keypad[CONF_BUTTONS]
|
||||
],
|
||||
CONF_NAME: keypad[CONF_NAME],
|
||||
}
|
||||
for keypad in config[CONF_KEYPADS]
|
||||
|
|
|
@ -4,11 +4,14 @@ from __future__ import annotations
|
|||
DOMAIN = "homeworks"
|
||||
|
||||
CONF_ADDR = "addr"
|
||||
CONF_BUTTONS = "buttons"
|
||||
CONF_CONTROLLER_ID = "controller_id"
|
||||
CONF_DIMMERS = "dimmers"
|
||||
CONF_INDEX = "index"
|
||||
CONF_KEYPADS = "keypads"
|
||||
CONF_NUMBER = "number"
|
||||
CONF_RATE = "rate"
|
||||
CONF_RELEASE_DELAY = "release_delay"
|
||||
|
||||
DEFAULT_BUTTON_NAME = "Homeworks button"
|
||||
DEFAULT_KEYPAD_NAME = "Homeworks keypad"
|
||||
|
|
|
@ -97,11 +97,6 @@ class HomeworksLight(HomeworksEntity, LightEntity):
|
|||
float((level * 100.0) / 255.0), self._rate, 0, self._addr
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str]:
|
||||
"""Supported attributes."""
|
||||
return {"homeworks_address": self._addr}
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Is the light on/off."""
|
||||
|
|
|
@ -31,5 +31,105 @@
|
|||
"description": "Add a Lutron Homeworks controller"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"duplicated_addr": "The specified address is already in use",
|
||||
"duplicated_number": "The specified number is already in use",
|
||||
"invalid_addr": "Invalid address"
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"menu_options": {
|
||||
"add_keypad": "Add keypad",
|
||||
"add_light": "Add light",
|
||||
"remove_keypad": "Remove keypad",
|
||||
"remove_light": "Remove light",
|
||||
"select_edit_keypad": "Configure keypad",
|
||||
"select_edit_light": "Configure light"
|
||||
}
|
||||
},
|
||||
"add_button": {
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"number": "Number",
|
||||
"release_delay": "Release delay"
|
||||
},
|
||||
"data_description": {
|
||||
"number": "Button number in the range 1 to 24",
|
||||
"release_delay": "Time between press and release, set to 0 to only press"
|
||||
},
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::add_keypad%]"
|
||||
},
|
||||
"add_keypad": {
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"addr": "Address"
|
||||
},
|
||||
"data_description": {
|
||||
"addr": "Keypad address, must be formatted as `[##:##:##:##]`"
|
||||
},
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::add_keypad%]"
|
||||
},
|
||||
"add_light": {
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"addr": "[%key:component::homeworks::options::step::add_keypad::data::addr%]",
|
||||
"rate": "Fade rate"
|
||||
},
|
||||
"data_description": {
|
||||
"addr": "Keypad address, must be formatted as `[##:##:##:##]`",
|
||||
"rate": "Time in seconds for the light to transition to a new brightness level"
|
||||
},
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::add_light%]"
|
||||
},
|
||||
"edit_button": {
|
||||
"data": {
|
||||
"release_delay": "[%key:component::homeworks::options::step::add_button::data::release_delay%]"
|
||||
},
|
||||
"data_description": {
|
||||
"release_delay": "[%key:component::homeworks::options::step::add_button::data_description::release_delay%]"
|
||||
},
|
||||
"title": "[%key:component::homeworks::options::step::edit_keypad::menu_options::select_edit_button%]"
|
||||
},
|
||||
"edit_keypad": {
|
||||
"menu_options": {
|
||||
"add_button": "Add button",
|
||||
"remove_button": "Remove button",
|
||||
"select_edit_button": "Configure button"
|
||||
}
|
||||
},
|
||||
"edit_light": {
|
||||
"data": {
|
||||
"rate": "[%key:component::homeworks::options::step::add_light::data::rate%]"
|
||||
},
|
||||
"data_description": {
|
||||
"rate": "[%key:component::homeworks::options::step::add_light::data_description::rate%]"
|
||||
},
|
||||
"description": "Select a light to configure",
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::select_edit_light%]"
|
||||
},
|
||||
"remove_button": {
|
||||
"description": "Select buttons to remove",
|
||||
"title": "[%key:component::homeworks::options::step::edit_keypad::menu_options::remove_button%]"
|
||||
},
|
||||
"remove_keypad": {
|
||||
"description": "Select keypads to remove",
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::remove_keypad%]"
|
||||
},
|
||||
"remove_light": {
|
||||
"description": "Select lights to remove",
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::remove_light%]"
|
||||
},
|
||||
"select_edit_button": {
|
||||
"title": "[%key:component::homeworks::options::step::edit_keypad::menu_options::select_edit_button%]"
|
||||
},
|
||||
"select_edit_keypad": {
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::select_edit_keypad%]"
|
||||
},
|
||||
"select_edit_light": {
|
||||
"title": "[%key:component::homeworks::options::step::init::menu_options::select_edit_light%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,13 @@ import pytest
|
|||
|
||||
from homeassistant.components.homeworks.const import (
|
||||
CONF_ADDR,
|
||||
CONF_BUTTONS,
|
||||
CONF_CONTROLLER_ID,
|
||||
CONF_DIMMERS,
|
||||
CONF_KEYPADS,
|
||||
CONF_NUMBER,
|
||||
CONF_RATE,
|
||||
CONF_RELEASE_DELAY,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
|
||||
|
@ -39,6 +42,23 @@ def mock_config_entry() -> MockConfigEntry:
|
|||
{
|
||||
CONF_ADDR: "[02:08:02:01]",
|
||||
CONF_NAME: "Foyer Keypad",
|
||||
CONF_BUTTONS: [
|
||||
{
|
||||
CONF_NAME: "Morning",
|
||||
CONF_NUMBER: 1,
|
||||
CONF_RELEASE_DELAY: None,
|
||||
},
|
||||
{
|
||||
CONF_NAME: "Relax",
|
||||
CONF_NUMBER: 2,
|
||||
CONF_RELEASE_DELAY: None,
|
||||
},
|
||||
{
|
||||
CONF_NAME: "Dim up",
|
||||
CONF_NUMBER: 3,
|
||||
CONF_RELEASE_DELAY: 0.2,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
|
|
|
@ -4,12 +4,16 @@ from unittest.mock import ANY, MagicMock
|
|||
import pytest
|
||||
from pytest_unordered import unordered
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
|
||||
from homeassistant.components.homeworks.const import (
|
||||
CONF_ADDR,
|
||||
CONF_BUTTONS,
|
||||
CONF_DIMMERS,
|
||||
CONF_INDEX,
|
||||
CONF_KEYPADS,
|
||||
CONF_NUMBER,
|
||||
CONF_RATE,
|
||||
CONF_RELEASE_DELAY,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
|
@ -154,6 +158,23 @@ async def test_import_flow(
|
|||
{
|
||||
CONF_ADDR: "[02:08:02:01]",
|
||||
CONF_NAME: "Foyer Keypad",
|
||||
CONF_BUTTONS: [
|
||||
{
|
||||
CONF_NAME: "Morning",
|
||||
CONF_NUMBER: 1,
|
||||
CONF_RELEASE_DELAY: None,
|
||||
},
|
||||
{
|
||||
CONF_NAME: "Relax",
|
||||
CONF_NUMBER: 2,
|
||||
CONF_RELEASE_DELAY: None,
|
||||
},
|
||||
{
|
||||
CONF_NAME: "Dim up",
|
||||
CONF_NUMBER: 3,
|
||||
CONF_RELEASE_DELAY: 0.2,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
|
@ -180,6 +201,15 @@ async def test_import_flow(
|
|||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
|
@ -320,6 +350,15 @@ async def test_options_add_remove_light_flow(
|
|||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
|
@ -362,6 +401,15 @@ async def test_options_add_remove_light_flow(
|
|||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
|
@ -412,9 +460,18 @@ async def test_options_add_remove_keypad_flow(
|
|||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
},
|
||||
{"addr": "[02:08:03:01]", "name": "Hall Keypad"},
|
||||
{"addr": "[02:08:03:01]", "buttons": [], "name": "Hall Keypad"},
|
||||
],
|
||||
"port": 1234,
|
||||
}
|
||||
|
@ -447,7 +504,7 @@ async def test_options_add_remove_keypad_flow(
|
|||
{"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0},
|
||||
],
|
||||
"host": "192.168.0.1",
|
||||
"keypads": [{"addr": "[02:08:03:01]", "name": "Hall Keypad"}],
|
||||
"keypads": [{"addr": "[02:08:03:01]", "buttons": [], "name": "Hall Keypad"}],
|
||||
"port": 1234,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
|
@ -551,6 +608,15 @@ async def test_options_edit_light_no_lights_flow(
|
|||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
|
@ -586,3 +652,285 @@ async def test_options_edit_light_flow_empty(
|
|||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_light"
|
||||
assert result["data_schema"].schema["index"].container == {}
|
||||
|
||||
|
||||
async def test_options_add_button_flow(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
|
||||
) -> None:
|
||||
"""Test options flow to add a button."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 3
|
||||
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "select_edit_keypad"}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_keypad"
|
||||
assert result["data_schema"].schema["index"].container == {
|
||||
"0": "Foyer Keypad ([02:08:02:01])"
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{"index": "0"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "edit_keypad"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "add_button"}
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "add_button"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "Dim down",
|
||||
CONF_NUMBER: 4,
|
||||
CONF_RELEASE_DELAY: 0.2,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"controller_id": "main_controller",
|
||||
"dimmers": [{"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0}],
|
||||
"host": "192.168.0.1",
|
||||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": None,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
{
|
||||
"name": "Dim down",
|
||||
"number": 4,
|
||||
"release_delay": 0.2,
|
||||
},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
"port": 1234,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the new entities were added
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 4
|
||||
|
||||
|
||||
async def test_options_add_button_flow_duplicate(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
|
||||
) -> None:
|
||||
"""Test options flow to add a button."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 3
|
||||
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "select_edit_keypad"}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_keypad"
|
||||
assert result["data_schema"].schema["index"].container == {
|
||||
"0": "Foyer Keypad ([02:08:02:01])"
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{"index": "0"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "edit_keypad"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "add_button"}
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "add_button"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "Dim down",
|
||||
CONF_NUMBER: 1,
|
||||
CONF_RELEASE_DELAY: 0.2,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "duplicated_number"}
|
||||
|
||||
|
||||
async def test_options_edit_button_flow(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
|
||||
) -> None:
|
||||
"""Test options flow to add a button."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 3
|
||||
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "select_edit_keypad"}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_keypad"
|
||||
assert result["data_schema"].schema["index"].container == {
|
||||
"0": "Foyer Keypad ([02:08:02:01])"
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{"index": "0"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "edit_keypad"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "select_edit_button"}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_button"
|
||||
assert result["data_schema"].schema["index"].container == {
|
||||
"0": "Morning (1)",
|
||||
"1": "Relax (2)",
|
||||
"2": "Dim up (3)",
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{"index": "0"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "edit_button"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_RELEASE_DELAY: 0,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"controller_id": "main_controller",
|
||||
"dimmers": [{"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0}],
|
||||
"host": "192.168.0.1",
|
||||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{
|
||||
"name": "Morning",
|
||||
"number": 1,
|
||||
"release_delay": 0.0,
|
||||
},
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
"port": 1234,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the new entities were added
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 3
|
||||
|
||||
|
||||
async def test_options_remove_button_flow(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_homeworks: MagicMock
|
||||
) -> None:
|
||||
"""Test options flow to remove a button."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 3
|
||||
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "select_edit_keypad"}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "select_edit_keypad"
|
||||
assert result["data_schema"].schema["index"].container == {
|
||||
"0": "Foyer Keypad ([02:08:02:01])"
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{"index": "0"},
|
||||
)
|
||||
assert result["type"] == FlowResultType.MENU
|
||||
assert result["step_id"] == "edit_keypad"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], {"next_step_id": "remove_button"}
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "remove_button"
|
||||
assert result["data_schema"].schema["index"].options == {
|
||||
"0": "Morning (1)",
|
||||
"1": "Relax (2)",
|
||||
"2": "Dim up (3)",
|
||||
}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={CONF_INDEX: ["0"]}
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"controller_id": "main_controller",
|
||||
"dimmers": [{"addr": "[02:08:01:01]", "name": "Foyer Sconces", "rate": 1.0}],
|
||||
"host": "192.168.0.1",
|
||||
"keypads": [
|
||||
{
|
||||
"addr": "[02:08:02:01]",
|
||||
"buttons": [
|
||||
{"name": "Relax", "number": 2, "release_delay": None},
|
||||
{"name": "Dim up", "number": 3, "release_delay": 0.2},
|
||||
],
|
||||
"name": "Foyer Keypad",
|
||||
}
|
||||
],
|
||||
"port": 1234,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check the entities were removed
|
||||
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 2
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue