Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add input_button * Update homeassistant/components/input_button/__init__.py Co-authored-by: Erik Montnemery <erik@montnemery.com> * Improve test coverage * Add reload test: not affecting state Co-authored-by: Erik Montnemery <erik@montnemery.com>
- Loading branch information
1 parent
ff062bd
commit fc6c0b1
Showing
13 changed files
with
576 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
"""Support to keep track of user controlled buttons which can be used in automations.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import cast | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.components.button import SERVICE_PRESS, ButtonEntity | ||
from homeassistant.const import ( | ||
ATTR_EDITABLE, | ||
CONF_ICON, | ||
CONF_ID, | ||
CONF_NAME, | ||
SERVICE_RELOAD, | ||
) | ||
from homeassistant.core import HomeAssistant, ServiceCall, callback | ||
from homeassistant.helpers import collection | ||
import homeassistant.helpers.config_validation as cv | ||
from homeassistant.helpers.entity_component import EntityComponent | ||
from homeassistant.helpers.restore_state import RestoreEntity | ||
import homeassistant.helpers.service | ||
from homeassistant.helpers.storage import Store | ||
from homeassistant.helpers.typing import ConfigType | ||
|
||
DOMAIN = "input_button" | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
CREATE_FIELDS = { | ||
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)), | ||
vol.Optional(CONF_ICON): cv.icon, | ||
} | ||
|
||
UPDATE_FIELDS = { | ||
vol.Optional(CONF_NAME): cv.string, | ||
vol.Optional(CONF_ICON): cv.icon, | ||
} | ||
|
||
CONFIG_SCHEMA = vol.Schema( | ||
{DOMAIN: cv.schema_with_slug_keys(vol.Any(UPDATE_FIELDS, None))}, | ||
extra=vol.ALLOW_EXTRA, | ||
) | ||
|
||
RELOAD_SERVICE_SCHEMA = vol.Schema({}) | ||
STORAGE_KEY = DOMAIN | ||
STORAGE_VERSION = 1 | ||
|
||
|
||
class InputButtonStorageCollection(collection.StorageCollection): | ||
"""Input button collection stored in storage.""" | ||
|
||
CREATE_SCHEMA = vol.Schema(CREATE_FIELDS) | ||
UPDATE_SCHEMA = vol.Schema(UPDATE_FIELDS) | ||
|
||
async def _process_create_data(self, data: dict) -> vol.Schema: | ||
"""Validate the config is valid.""" | ||
return self.CREATE_SCHEMA(data) | ||
|
||
@callback | ||
def _get_suggested_id(self, info: dict) -> str: | ||
"""Suggest an ID based on the config.""" | ||
return cast(str, info[CONF_NAME]) | ||
|
||
async def _update_data(self, data: dict, update_data: dict) -> dict: | ||
"""Return a new updated data object.""" | ||
update_data = self.UPDATE_SCHEMA(update_data) | ||
return {**data, **update_data} | ||
|
||
|
||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: | ||
"""Set up an input button.""" | ||
component = EntityComponent(_LOGGER, DOMAIN, hass) | ||
id_manager = collection.IDManager() | ||
|
||
yaml_collection = collection.YamlCollection( | ||
logging.getLogger(f"{__name__}.yaml_collection"), id_manager | ||
) | ||
collection.sync_entity_lifecycle( | ||
hass, DOMAIN, DOMAIN, component, yaml_collection, InputButton.from_yaml | ||
) | ||
|
||
storage_collection = InputButtonStorageCollection( | ||
Store(hass, STORAGE_VERSION, STORAGE_KEY), | ||
logging.getLogger(f"{__name__}.storage_collection"), | ||
id_manager, | ||
) | ||
collection.sync_entity_lifecycle( | ||
hass, DOMAIN, DOMAIN, component, storage_collection, InputButton | ||
) | ||
|
||
await yaml_collection.async_load( | ||
[{CONF_ID: id_, **(conf or {})} for id_, conf in config.get(DOMAIN, {}).items()] | ||
) | ||
await storage_collection.async_load() | ||
|
||
collection.StorageCollectionWebsocket( | ||
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS | ||
).async_setup(hass) | ||
|
||
async def reload_service_handler(service_call: ServiceCall) -> None: | ||
"""Remove all input buttons and load new ones from config.""" | ||
conf = await component.async_prepare_reload(skip_reset=True) | ||
if conf is None: | ||
return | ||
await yaml_collection.async_load( | ||
[ | ||
{CONF_ID: id_, **(conf or {})} | ||
for id_, conf in conf.get(DOMAIN, {}).items() | ||
] | ||
) | ||
|
||
homeassistant.helpers.service.async_register_admin_service( | ||
hass, | ||
DOMAIN, | ||
SERVICE_RELOAD, | ||
reload_service_handler, | ||
schema=RELOAD_SERVICE_SCHEMA, | ||
) | ||
|
||
component.async_register_entity_service(SERVICE_PRESS, {}, "_async_press_action") | ||
|
||
return True | ||
|
||
|
||
class InputButton(ButtonEntity, RestoreEntity): | ||
"""Representation of a button.""" | ||
|
||
_attr_should_poll = False | ||
|
||
def __init__(self, config: ConfigType) -> None: | ||
"""Initialize a button.""" | ||
self._config = config | ||
self.editable = True | ||
self._attr_unique_id = config[CONF_ID] | ||
|
||
@classmethod | ||
def from_yaml(cls, config: ConfigType) -> ButtonEntity: | ||
"""Return entity instance initialized from yaml storage.""" | ||
button = cls(config) | ||
button.entity_id = f"{DOMAIN}.{config[CONF_ID]}" | ||
button.editable = False | ||
return button | ||
|
||
@property | ||
def name(self) -> str | None: | ||
"""Return name of the button.""" | ||
return self._config.get(CONF_NAME) | ||
|
||
@property | ||
def icon(self) -> str | None: | ||
"""Return the icon to be used for this entity.""" | ||
return self._config.get(CONF_ICON) | ||
|
||
@property | ||
def extra_state_attributes(self) -> dict[str, bool]: | ||
"""Return the state attributes of the entity.""" | ||
return {ATTR_EDITABLE: self.editable} | ||
|
||
async def async_press(self) -> None: | ||
"""Press the button. | ||
Left emtpty intentionally. | ||
The input button itself doesn't trigger anything. | ||
""" | ||
return None | ||
|
||
async def async_update_config(self, config: ConfigType) -> None: | ||
"""Handle when the config is updated.""" | ||
self._config = config | ||
self.async_write_ha_state() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"domain": "input_button", | ||
"name": "Input Button", | ||
"documentation": "https://www.home-assistant.io/integrations/input_button", | ||
"codeowners": ["@home-assistant/core"], | ||
"quality_scale": "internal" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
press: | ||
name: Press | ||
description: Press the input button entity. | ||
target: | ||
entity: | ||
domain: input_button |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Tests for the input_test component.""" |
Oops, something went wrong.