homeassistant/custom_components/variable/__init__.py

207 lines
6.7 KiB
Python
Raw Normal View History

import asyncio
import logging
import json
import voluptuous as vol
from homeassistant.const import (CONF_NAME, ATTR_ICON)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import template
from homeassistant.exceptions import TemplateError
from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'variable'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_ATTRIBUTES = "attributes"
CONF_VALUE = "value"
CONF_RESTORE = "restore"
ATTR_VARIABLE = "variable"
ATTR_VALUE = 'value'
ATTR_VALUE_TEMPLATE = 'value_template'
ATTR_ATTRIBUTES = "attributes"
ATTR_ATTRIBUTES_TEMPLATE = "attributes_template"
ATTR_REPLACE_ATTRIBUTES = "replace_attributes"
SERVICE_SET_VARIABLE = "set_variable"
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)
@bind_hass
def set_variable(hass, variable, value, value_template, attributes, attributes_template, replace_attributes):
"""Set input_boolean to True."""
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,
})
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)
entities.append(Variable(variable_id, name, value, attributes, restore))
@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:
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]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
else:
_LOGGER.warning('Failed to set unknown variable: %s', entity_id)
hass.services.async_register(
DOMAIN, SERVICE_SET_VARIABLE, async_set_variable_service,
schema=SERVICE_SET_VARIABLE_SCHEMA)
await component.async_add_entities(entities)
return True
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()
if self._restore == True:
state = await self.async_get_last_state()
if state:
self._value = state.state
@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)
else:
return None
@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
def async_set_variable(self, value, value_template, attributes, attributes_template, replace_attributes):
"""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:
attributes = json.loads(attributes_template.async_render({ 'variable': current_state }))
if isinstance(attributes, dict):
if updated_attributes is not None:
updated_attributes.update(attributes)
else:
updated_attributes = attributes
except TemplateError as ex:
_LOGGER.error('Could not render attribute_template %s: %s',
self.entity_id, ex)
if value is not None:
updated_value = value
elif value_template is not None:
try:
value_template.hass = self.hass
updated_value = value_template.async_render({ 'variable': current_state })
except TemplateError as ex:
_LOGGER.error('Could not render value_template %s: %s',
self.entity_id, ex)
self._attributes = updated_attributes;
if updated_value is not None:
self._value = updated_value;
yield from self.async_update_ha_state()