Manual trigger entity and refactor command_line switch (#91506)

* TriggerEntity to CoordinatorTriggerEntity

* _render_templates

* split manual vs coordinator

* name

* ManualTriggerEntity

* value

* Remove ManualTriggerEntity

* ManualTriggerEntity

* process_manual_data

* Add test

* imports

* Move ManualTriggerEntity

* cmd_line switch

* Review comments

* Fix templating

* Review comments

* Remove unneeded logging
This commit is contained in:
G Johansson
2023-05-08 10:19:37 +02:00
committed by GitHub
parent 2e65b77b2b
commit 6ad4e13b38
4 changed files with 147 additions and 23 deletions
+18 -21
View File
@@ -16,7 +16,9 @@ from homeassistant.const import (
CONF_COMMAND_ON,
CONF_COMMAND_STATE,
CONF_FRIENDLY_NAME,
CONF_ICON,
CONF_ICON_TEMPLATE,
CONF_NAME,
CONF_SWITCHES,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
@@ -26,6 +28,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.template import Template
from homeassistant.helpers.template_entity import ManualTriggerEntity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN, PLATFORMS
@@ -65,26 +68,26 @@ async def async_setup_platform(
switches = []
for object_id, device_config in devices.items():
trigger_entity_config = {
CONF_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
CONF_NAME: Template(device_config.get(CONF_FRIENDLY_NAME, object_id), hass),
CONF_ICON: device_config.get(CONF_ICON_TEMPLATE),
}
value_template: Template | None = device_config.get(CONF_VALUE_TEMPLATE)
if value_template is not None:
value_template.hass = hass
icon_template: Template | None = device_config.get(CONF_ICON_TEMPLATE)
if icon_template is not None:
icon_template.hass = hass
switches.append(
CommandSwitch(
trigger_entity_config,
object_id,
device_config.get(CONF_FRIENDLY_NAME, object_id),
device_config[CONF_COMMAND_ON],
device_config[CONF_COMMAND_OFF],
device_config.get(CONF_COMMAND_STATE),
icon_template,
value_template,
device_config[CONF_COMMAND_TIMEOUT],
device_config.get(CONF_UNIQUE_ID),
)
)
@@ -95,32 +98,28 @@ async def async_setup_platform(
async_add_entities(switches)
class CommandSwitch(SwitchEntity):
class CommandSwitch(ManualTriggerEntity, SwitchEntity):
"""Representation a switch that can be toggled using shell commands."""
def __init__(
self,
config: ConfigType,
object_id: str,
friendly_name: str,
command_on: str,
command_off: str,
command_state: str | None,
icon_template: Template | None,
value_template: Template | None,
timeout: int,
unique_id: str | None,
) -> None:
"""Initialize the switch."""
super().__init__(self.hass, config)
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._attr_name = friendly_name
self._attr_is_on = False
self._command_on = command_on
self._command_off = command_off
self._command_state = command_state
self._icon_template = icon_template
self._value_template = value_template
self._timeout = timeout
self._attr_unique_id = unique_id
self._attr_should_poll = bool(command_state)
async def _switch(self, command: str) -> bool:
@@ -169,17 +168,15 @@ class CommandSwitch(SwitchEntity):
"""Update device state."""
if self._command_state:
payload = str(await self.hass.async_add_executor_job(self._query_state))
if self._icon_template:
self._attr_icon = (
self._icon_template.async_render_with_possible_json_value(payload)
)
value = None
if self._value_template:
payload = self._value_template.async_render_with_possible_json_value(
value = self._value_template.async_render_with_possible_json_value(
payload, None
)
self._attr_is_on = None
if payload:
self._attr_is_on = payload.lower() == "true"
if payload or value:
self._attr_is_on = (value or payload).lower() == "true"
self._process_manual_data(payload)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
+35 -2
View File
@@ -29,6 +29,7 @@ from homeassistant.const import (
)
from homeassistant.core import Context, CoreState, Event, HomeAssistant, State, callback
from homeassistant.exceptions import TemplateError
from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads
from . import config_validation as cv
from .entity import Entity
@@ -487,9 +488,8 @@ class TriggerBaseEntity(Entity):
CONF_NAME,
CONF_PICTURE,
):
if itm not in config:
if itm not in config or config[itm] is None:
continue
if config[itm].is_static:
self._static_rendered[itm] = config[itm].template
else:
@@ -597,3 +597,36 @@ class TriggerBaseEntity(Entity):
"Error rendering %s template for %s: %s", key, self.entity_id, err
)
self._rendered = self._static_rendered
class ManualTriggerEntity(TriggerBaseEntity):
"""Template entity based on manual trigger data."""
def __init__(
self,
hass: HomeAssistant,
config: dict,
) -> None:
"""Initialize the entity."""
TriggerBaseEntity.__init__(self, hass, config)
@callback
def _process_manual_data(self, value: str | None = None) -> None:
"""Process new data manually.
Implementing class should call this last in update method to render templates.
Ex: self._process_manual_data(payload)
"""
self.async_write_ha_state()
this = None
if state := self.hass.states.get(self.entity_id):
this = state.as_dict()
run_variables: dict[str, Any] = {"value": value}
# Silently try if variable is a json and store result in `value_json` if it is.
with contextlib.suppress(*JSON_DECODE_EXCEPTIONS):
run_variables["value_json"] = json_loads(run_variables["value"])
variables = {"this": this, **(run_variables or {})}
self._render_templates(variables)