2020-12-08 19:58:32 -08:00
|
|
|
"""variable implementation for Homme Assistant."""
|
2020-03-27 08:13:30 -07:00
|
|
|
import asyncio
|
|
|
|
import logging
|
|
|
|
import json
|
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
from homeassistant.const import CONF_NAME, ATTR_ICON
|
2020-03-27 08:13:30 -07:00
|
|
|
from homeassistant.helpers import config_validation as cv
|
|
|
|
from homeassistant.exceptions import TemplateError
|
|
|
|
from homeassistant.loader import bind_hass
|
|
|
|
from homeassistant.helpers.entity_component import EntityComponent
|
|
|
|
from homeassistant.helpers.restore_state import RestoreEntity
|
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
DOMAIN = "variable"
|
|
|
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
CONF_ATTRIBUTES = "attributes"
|
|
|
|
CONF_VALUE = "value"
|
|
|
|
CONF_RESTORE = "restore"
|
|
|
|
|
|
|
|
ATTR_VARIABLE = "variable"
|
2020-12-08 19:58:32 -08:00
|
|
|
ATTR_VALUE = "value"
|
|
|
|
ATTR_VALUE_TEMPLATE = "value_template"
|
2020-03-27 08:13:30 -07:00
|
|
|
ATTR_ATTRIBUTES = "attributes"
|
|
|
|
ATTR_ATTRIBUTES_TEMPLATE = "attributes_template"
|
|
|
|
ATTR_REPLACE_ATTRIBUTES = "replace_attributes"
|
|
|
|
|
|
|
|
SERVICE_SET_VARIABLE = "set_variable"
|
2020-12-08 19:58:32 -08:00
|
|
|
SERVICE_SET_VARIABLE_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(ATTR_VARIABLE): cv.string,
|
|
|
|
vol.Optional(ATTR_VALUE): cv.match_all,
|
|
|
|
vol.Optional(ATTR_VALUE_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(ATTR_ATTRIBUTES): dict,
|
|
|
|
vol.Optional(ATTR_ATTRIBUTES_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(ATTR_REPLACE_ATTRIBUTES): cv.boolean,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
DOMAIN: vol.Schema(
|
|
|
|
{
|
|
|
|
cv.slug: vol.Any(
|
|
|
|
{
|
|
|
|
vol.Optional(CONF_NAME): cv.string,
|
|
|
|
vol.Optional(CONF_VALUE): cv.match_all,
|
|
|
|
vol.Optional(CONF_ATTRIBUTES): dict,
|
|
|
|
vol.Optional(CONF_RESTORE): cv.boolean,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
extra=vol.ALLOW_EXTRA,
|
|
|
|
)
|
|
|
|
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
@bind_hass
|
2020-12-08 19:58:32 -08:00
|
|
|
def set_variable(
|
|
|
|
hass,
|
|
|
|
variable,
|
|
|
|
value,
|
|
|
|
value_template,
|
|
|
|
attributes,
|
|
|
|
attributes_template,
|
|
|
|
replace_attributes,
|
|
|
|
):
|
2020-03-27 08:13:30 -07:00
|
|
|
"""Set input_boolean to True."""
|
2020-12-08 19:58:32 -08:00
|
|
|
hass.services.call(
|
|
|
|
DOMAIN,
|
|
|
|
SERVICE_SET_VARIABLE,
|
|
|
|
{
|
|
|
|
ATTR_VARIABLE: variable,
|
|
|
|
ATTR_VALUE: value,
|
|
|
|
ATTR_VALUE_TEMPLATE: value_template,
|
|
|
|
ATTR_ATTRIBUTES: attributes,
|
|
|
|
ATTR_ATTRIBUTES_TEMPLATE: attributes_template,
|
|
|
|
ATTR_REPLACE_ATTRIBUTES: replace_attributes,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
async def async_setup(hass, config):
|
|
|
|
"""Set up variables."""
|
|
|
|
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
|
|
|
|
|
|
|
entities = []
|
|
|
|
|
|
|
|
for variable_id, variable_config in config[DOMAIN].items():
|
|
|
|
if not variable_config:
|
|
|
|
variable_config = {}
|
|
|
|
|
|
|
|
name = variable_config.get(CONF_NAME)
|
|
|
|
value = variable_config.get(CONF_VALUE)
|
|
|
|
attributes = variable_config.get(CONF_ATTRIBUTES)
|
|
|
|
restore = variable_config.get(CONF_RESTORE, False)
|
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
entities.append(
|
|
|
|
Variable(variable_id, name, value, attributes, restore)
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def async_set_variable_service(call):
|
|
|
|
"""Handle calls to the set_variable service."""
|
|
|
|
entity_id = ENTITY_ID_FORMAT.format(call.data.get(ATTR_VARIABLE))
|
|
|
|
entity = component.get_entity(entity_id)
|
|
|
|
|
|
|
|
if entity:
|
2020-12-08 19:58:32 -08:00
|
|
|
target_variables = [entity]
|
|
|
|
tasks = [
|
|
|
|
variable.async_set_variable(
|
|
|
|
call.data.get(ATTR_VALUE),
|
|
|
|
call.data.get(ATTR_VALUE_TEMPLATE),
|
|
|
|
call.data.get(ATTR_ATTRIBUTES),
|
|
|
|
call.data.get(ATTR_ATTRIBUTES_TEMPLATE),
|
|
|
|
call.data.get(ATTR_REPLACE_ATTRIBUTES, False),
|
|
|
|
)
|
|
|
|
for variable in target_variables
|
|
|
|
]
|
2020-03-27 08:13:30 -07:00
|
|
|
if tasks:
|
|
|
|
yield from asyncio.wait(tasks, loop=hass.loop)
|
|
|
|
|
|
|
|
else:
|
2020-12-08 19:58:32 -08:00
|
|
|
_LOGGER.warning("Failed to set unknown variable: %s", entity_id)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
hass.services.async_register(
|
2020-12-08 19:58:32 -08:00
|
|
|
DOMAIN,
|
|
|
|
SERVICE_SET_VARIABLE,
|
|
|
|
async_set_variable_service,
|
|
|
|
schema=SERVICE_SET_VARIABLE_SCHEMA,
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
await component.async_add_entities(entities)
|
|
|
|
return True
|
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
|
2020-03-27 08:13:30 -07:00
|
|
|
class Variable(RestoreEntity):
|
|
|
|
"""Representation of a variable."""
|
|
|
|
|
|
|
|
def __init__(self, variable_id, name, value, attributes, restore):
|
|
|
|
"""Initialize a variable."""
|
|
|
|
self.entity_id = ENTITY_ID_FORMAT.format(variable_id)
|
|
|
|
self._name = name
|
|
|
|
self._value = value
|
|
|
|
self._attributes = attributes
|
|
|
|
self._restore = restore
|
|
|
|
|
|
|
|
async def async_added_to_hass(self):
|
|
|
|
"""Run when entity about to be added."""
|
|
|
|
await super().async_added_to_hass()
|
2020-12-08 19:58:32 -08:00
|
|
|
if self._restore is True:
|
|
|
|
# If variable state have been saved.
|
2020-03-27 08:13:30 -07:00
|
|
|
state = await self.async_get_last_state()
|
|
|
|
if state:
|
2020-12-08 19:58:32 -08:00
|
|
|
# restore state
|
2020-03-27 08:13:30 -07:00
|
|
|
self._value = state.state
|
2020-12-08 19:58:32 -08:00
|
|
|
# restore value
|
|
|
|
self._attributes = state.attributes
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""If entity should be polled."""
|
|
|
|
return False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return the name of the variable."""
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def icon(self):
|
|
|
|
"""Return the icon to be used for this entity."""
|
|
|
|
if self._attributes is not None:
|
|
|
|
return self._attributes.get(ATTR_ICON)
|
2020-12-08 19:58:32 -08:00
|
|
|
return None
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
@property
|
|
|
|
def state(self):
|
|
|
|
"""Return the state of the component."""
|
|
|
|
return self._value
|
|
|
|
|
|
|
|
@property
|
|
|
|
def state_attributes(self):
|
|
|
|
"""Return the state attributes."""
|
|
|
|
return self._attributes
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
2020-12-08 19:58:32 -08:00
|
|
|
def async_set_variable(
|
|
|
|
self,
|
|
|
|
value,
|
|
|
|
value_template,
|
|
|
|
attributes,
|
|
|
|
attributes_template,
|
|
|
|
replace_attributes,
|
|
|
|
):
|
2020-03-27 08:13:30 -07:00
|
|
|
"""Update variable."""
|
|
|
|
current_state = self.hass.states.get(self.entity_id)
|
|
|
|
updated_attributes = None
|
|
|
|
updated_value = None
|
|
|
|
|
|
|
|
if not replace_attributes and self._attributes is not None:
|
|
|
|
updated_attributes = dict(self._attributes)
|
|
|
|
|
|
|
|
if attributes is not None:
|
|
|
|
if updated_attributes is not None:
|
|
|
|
updated_attributes.update(attributes)
|
|
|
|
else:
|
|
|
|
updated_attributes = attributes
|
|
|
|
|
|
|
|
elif attributes_template is not None:
|
|
|
|
attributes_template.hass = self.hass
|
|
|
|
|
|
|
|
try:
|
2020-12-08 19:58:32 -08:00
|
|
|
attributes = json.loads(
|
|
|
|
attributes_template.async_render(
|
|
|
|
{"variable": current_state}
|
|
|
|
)
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
if isinstance(attributes, dict):
|
|
|
|
if updated_attributes is not None:
|
|
|
|
updated_attributes.update(attributes)
|
|
|
|
else:
|
|
|
|
updated_attributes = attributes
|
|
|
|
|
|
|
|
except TemplateError as ex:
|
2020-12-08 19:58:32 -08:00
|
|
|
_LOGGER.error(
|
|
|
|
"Could not render attribute_template %s: %s",
|
|
|
|
self.entity_id,
|
|
|
|
ex,
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
if value is not None:
|
|
|
|
updated_value = value
|
|
|
|
|
|
|
|
elif value_template is not None:
|
|
|
|
try:
|
|
|
|
value_template.hass = self.hass
|
2020-12-08 19:58:32 -08:00
|
|
|
updated_value = value_template.async_render(
|
|
|
|
{"variable": current_state}
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
except TemplateError as ex:
|
2020-12-08 19:58:32 -08:00
|
|
|
_LOGGER.error(
|
|
|
|
"Could not render value_template %s: %s",
|
|
|
|
self.entity_id,
|
|
|
|
ex,
|
|
|
|
)
|
2020-03-27 08:13:30 -07:00
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
self._attributes = updated_attributes
|
2020-03-27 08:13:30 -07:00
|
|
|
|
|
|
|
if updated_value is not None:
|
2020-12-08 19:58:32 -08:00
|
|
|
self._value = updated_value
|
2020-03-27 08:13:30 -07:00
|
|
|
|
2020-12-08 19:58:32 -08:00
|
|
|
yield from self.async_update_ha_state()
|