2019-04-03 17:40:03 +02:00
|
|
|
"""Allows the creation of a sensor that breaks out state_attributes."""
|
2021-03-18 14:43:52 +01:00
|
|
|
from __future__ import annotations
|
2016-09-08 16:26:54 +02:00
|
|
|
|
2016-08-22 09:11:16 +01:00
|
|
|
import voluptuous as vol
|
2016-01-21 16:31:23 +00:00
|
|
|
|
2019-07-31 12:25:30 -07:00
|
|
|
from homeassistant.components.sensor import (
|
2019-12-08 21:05:08 +01:00
|
|
|
DEVICE_CLASSES_SCHEMA,
|
2019-07-31 12:25:30 -07:00
|
|
|
ENTITY_ID_FORMAT,
|
|
|
|
PLATFORM_SCHEMA,
|
2021-03-22 19:47:44 +01:00
|
|
|
SensorEntity,
|
2019-07-31 12:25:30 -07:00
|
|
|
)
|
2016-01-21 16:31:23 +00:00
|
|
|
from homeassistant.const import (
|
2019-12-08 21:05:08 +01:00
|
|
|
ATTR_ENTITY_ID,
|
|
|
|
CONF_DEVICE_CLASS,
|
2019-07-31 12:25:30 -07:00
|
|
|
CONF_ENTITY_PICTURE_TEMPLATE,
|
2021-03-29 09:57:51 -07:00
|
|
|
CONF_FRIENDLY_NAME,
|
2019-12-08 21:05:08 +01:00
|
|
|
CONF_FRIENDLY_NAME_TEMPLATE,
|
|
|
|
CONF_ICON_TEMPLATE,
|
2019-07-31 12:25:30 -07:00
|
|
|
CONF_SENSORS,
|
2020-08-02 00:45:55 +02:00
|
|
|
CONF_UNIQUE_ID,
|
2021-03-29 09:57:51 -07:00
|
|
|
CONF_UNIT_OF_MEASUREMENT,
|
2019-12-08 21:05:08 +01:00
|
|
|
CONF_VALUE_TEMPLATE,
|
2019-07-31 12:25:30 -07:00
|
|
|
)
|
2019-12-08 21:05:08 +01:00
|
|
|
from homeassistant.core import callback
|
2016-01-21 23:17:19 +00:00
|
|
|
from homeassistant.exceptions import TemplateError
|
2021-03-29 09:57:51 -07:00
|
|
|
from homeassistant.helpers import config_validation as cv
|
2021-03-22 19:47:44 +01:00
|
|
|
from homeassistant.helpers.entity import async_generate_entity_id
|
2019-12-08 21:05:08 +01:00
|
|
|
|
2021-03-29 09:57:51 -07:00
|
|
|
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE, CONF_TRIGGER
|
2020-08-21 07:33:53 -05:00
|
|
|
from .template_entity import TemplateEntity
|
2021-03-29 09:57:51 -07:00
|
|
|
from .trigger_entity import TriggerEntity
|
2019-09-01 17:12:55 +01:00
|
|
|
|
2020-09-01 08:53:50 -05:00
|
|
|
SENSOR_SCHEMA = vol.All(
|
|
|
|
cv.deprecated(ATTR_ENTITY_ID),
|
|
|
|
vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(CONF_ICON_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(CONF_FRIENDLY_NAME_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
|
|
|
|
vol.Optional(CONF_ATTRIBUTE_TEMPLATES, default={}): vol.Schema(
|
|
|
|
{cv.string: cv.template}
|
|
|
|
),
|
2021-03-29 09:57:51 -07:00
|
|
|
vol.Optional(CONF_FRIENDLY_NAME): cv.string,
|
|
|
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
2020-09-01 08:53:50 -05:00
|
|
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
|
|
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
|
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
|
|
|
}
|
|
|
|
),
|
2019-07-31 12:25:30 -07:00
|
|
|
)
|
|
|
|
|
2021-03-29 09:57:51 -07:00
|
|
|
|
|
|
|
def trigger_warning(val):
|
|
|
|
"""Warn if a trigger is defined."""
|
|
|
|
if CONF_TRIGGER in val:
|
|
|
|
raise vol.Invalid(
|
|
|
|
"You can only add triggers to template entities if they are defined under `template:`. "
|
|
|
|
"See the template documentation for more information: https://www.home-assistant.io/integrations/template/"
|
|
|
|
)
|
|
|
|
|
|
|
|
return val
|
|
|
|
|
|
|
|
|
|
|
|
PLATFORM_SCHEMA = vol.All(
|
|
|
|
PLATFORM_SCHEMA.extend(
|
|
|
|
{
|
|
|
|
vol.Optional(CONF_TRIGGER): cv.match_all, # to raise custom warning
|
|
|
|
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(SENSOR_SCHEMA),
|
|
|
|
}
|
|
|
|
),
|
|
|
|
trigger_warning,
|
2019-07-31 12:25:30 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-03-29 09:57:51 -07:00
|
|
|
@callback
|
|
|
|
def _async_create_template_tracking_entities(hass, config):
|
2020-08-21 18:31:48 -05:00
|
|
|
"""Create the template sensors."""
|
2016-01-21 16:31:23 +00:00
|
|
|
sensors = []
|
|
|
|
|
2016-01-21 23:17:19 +00:00
|
|
|
for device, device_config in config[CONF_SENSORS].items():
|
2016-08-23 07:56:39 +01:00
|
|
|
state_template = device_config[CONF_VALUE_TEMPLATE]
|
2017-02-07 03:51:44 -06:00
|
|
|
icon_template = device_config.get(CONF_ICON_TEMPLATE)
|
2019-07-31 12:25:30 -07:00
|
|
|
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
|
2019-09-25 00:05:19 +10:00
|
|
|
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
|
2021-03-29 09:57:51 -07:00
|
|
|
friendly_name = device_config.get(CONF_FRIENDLY_NAME, device)
|
2018-02-11 21:12:30 +01:00
|
|
|
friendly_name_template = device_config.get(CONF_FRIENDLY_NAME_TEMPLATE)
|
2021-03-29 09:57:51 -07:00
|
|
|
unit_of_measurement = device_config.get(CONF_UNIT_OF_MEASUREMENT)
|
2018-05-01 20:32:44 +02:00
|
|
|
device_class = device_config.get(CONF_DEVICE_CLASS)
|
2019-09-01 17:12:55 +01:00
|
|
|
attribute_templates = device_config[CONF_ATTRIBUTE_TEMPLATES]
|
2020-08-02 00:45:55 +02:00
|
|
|
unique_id = device_config.get(CONF_UNIQUE_ID)
|
2016-05-29 23:34:21 +02:00
|
|
|
|
2016-01-21 16:31:23 +00:00
|
|
|
sensors.append(
|
|
|
|
SensorTemplate(
|
|
|
|
hass,
|
2016-02-01 17:45:18 +00:00
|
|
|
device,
|
2016-01-21 16:31:23 +00:00
|
|
|
friendly_name,
|
2018-02-11 21:12:30 +01:00
|
|
|
friendly_name_template,
|
2016-01-21 16:31:23 +00:00
|
|
|
unit_of_measurement,
|
2016-05-29 23:34:21 +02:00
|
|
|
state_template,
|
2017-02-07 03:51:44 -06:00
|
|
|
icon_template,
|
2017-10-30 10:28:37 -06:00
|
|
|
entity_picture_template,
|
2019-09-25 00:05:19 +10:00
|
|
|
availability_template,
|
2019-07-31 12:25:30 -07:00
|
|
|
device_class,
|
2019-09-01 17:12:55 +01:00
|
|
|
attribute_templates,
|
2020-08-02 00:45:55 +02:00
|
|
|
unique_id,
|
2016-01-21 16:31:23 +00:00
|
|
|
)
|
2019-07-31 12:25:30 -07:00
|
|
|
)
|
2019-11-26 11:30:49 +11:00
|
|
|
|
2020-08-21 18:31:48 -05:00
|
|
|
return sensors
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
|
|
|
"""Set up the template sensors."""
|
2021-03-29 09:57:51 -07:00
|
|
|
if discovery_info is None:
|
|
|
|
async_add_entities(_async_create_template_tracking_entities(hass, config))
|
|
|
|
else:
|
|
|
|
async_add_entities(
|
|
|
|
TriggerSensorEntity(hass, discovery_info["coordinator"], device_id, config)
|
|
|
|
for device_id, config in discovery_info["entities"].items()
|
|
|
|
)
|
2016-01-21 16:31:23 +00:00
|
|
|
|
|
|
|
|
2021-03-22 19:47:44 +01:00
|
|
|
class SensorTemplate(TemplateEntity, SensorEntity):
|
2016-03-08 16:46:34 +01:00
|
|
|
"""Representation of a Template Sensor."""
|
2016-01-21 16:31:23 +00:00
|
|
|
|
2019-07-31 12:25:30 -07:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
hass,
|
|
|
|
device_id,
|
|
|
|
friendly_name,
|
|
|
|
friendly_name_template,
|
|
|
|
unit_of_measurement,
|
|
|
|
state_template,
|
|
|
|
icon_template,
|
|
|
|
entity_picture_template,
|
2019-09-25 00:05:19 +10:00
|
|
|
availability_template,
|
2019-07-31 12:25:30 -07:00
|
|
|
device_class,
|
2019-09-01 17:12:55 +01:00
|
|
|
attribute_templates,
|
2020-08-02 00:45:55 +02:00
|
|
|
unique_id,
|
2019-07-31 12:25:30 -07:00
|
|
|
):
|
2016-03-08 16:46:34 +01:00
|
|
|
"""Initialize the sensor."""
|
2020-08-20 09:07:58 -05:00
|
|
|
super().__init__(
|
2020-08-21 07:33:53 -05:00
|
|
|
attribute_templates=attribute_templates,
|
|
|
|
availability_template=availability_template,
|
|
|
|
icon_template=icon_template,
|
|
|
|
entity_picture_template=entity_picture_template,
|
2020-08-20 09:07:58 -05:00
|
|
|
)
|
2019-07-31 12:25:30 -07:00
|
|
|
self.entity_id = async_generate_entity_id(
|
|
|
|
ENTITY_ID_FORMAT, device_id, hass=hass
|
|
|
|
)
|
2016-01-21 23:17:19 +00:00
|
|
|
self._name = friendly_name
|
2018-02-11 21:12:30 +01:00
|
|
|
self._friendly_name_template = friendly_name_template
|
2016-01-21 16:31:23 +00:00
|
|
|
self._unit_of_measurement = unit_of_measurement
|
2016-09-27 21:29:55 -07:00
|
|
|
self._template = state_template
|
2016-03-24 23:07:19 -07:00
|
|
|
self._state = None
|
2018-05-01 20:32:44 +02:00
|
|
|
self._device_class = device_class
|
2020-08-20 09:07:58 -05:00
|
|
|
|
2020-08-02 00:45:55 +02:00
|
|
|
self._unique_id = unique_id
|
2017-03-02 06:38:19 +01:00
|
|
|
|
2018-10-01 08:55:43 +02:00
|
|
|
async def async_added_to_hass(self):
|
2017-03-02 06:38:19 +01:00
|
|
|
"""Register callbacks."""
|
2020-08-20 08:06:41 -05:00
|
|
|
self.add_template_attribute("_state", self._template, None, self._update_state)
|
|
|
|
if self._friendly_name_template is not None:
|
|
|
|
self.add_template_attribute("_name", self._friendly_name_template)
|
2017-03-02 06:38:19 +01:00
|
|
|
|
2020-08-20 08:06:41 -05:00
|
|
|
await super().async_added_to_hass()
|
2016-03-24 23:07:19 -07:00
|
|
|
|
2020-08-20 08:06:41 -05:00
|
|
|
@callback
|
|
|
|
def _update_state(self, result):
|
2020-08-20 08:32:52 -05:00
|
|
|
super()._update_state(result)
|
|
|
|
self._state = None if isinstance(result, TemplateError) else result
|
2016-01-21 16:31:23 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
2016-03-08 16:46:34 +01:00
|
|
|
"""Return the name of the sensor."""
|
2016-01-21 16:31:23 +00:00
|
|
|
return self._name
|
|
|
|
|
2020-08-02 00:45:55 +02:00
|
|
|
@property
|
|
|
|
def unique_id(self):
|
|
|
|
"""Return the unique id of this sensor."""
|
|
|
|
return self._unique_id
|
|
|
|
|
2016-01-21 16:31:23 +00:00
|
|
|
@property
|
|
|
|
def state(self):
|
2016-03-08 16:46:34 +01:00
|
|
|
"""Return the state of the sensor."""
|
2016-01-21 16:31:23 +00:00
|
|
|
return self._state
|
|
|
|
|
2018-05-01 20:32:44 +02:00
|
|
|
@property
|
2021-03-18 14:43:52 +01:00
|
|
|
def device_class(self) -> str | None:
|
2018-05-01 20:32:44 +02:00
|
|
|
"""Return the device class of the sensor."""
|
|
|
|
return self._device_class
|
|
|
|
|
2016-01-21 16:31:23 +00:00
|
|
|
@property
|
|
|
|
def unit_of_measurement(self):
|
2016-03-08 16:46:34 +01:00
|
|
|
"""Return the unit_of_measurement of the device."""
|
2016-01-21 16:31:23 +00:00
|
|
|
return self._unit_of_measurement
|
2021-03-29 09:57:51 -07:00
|
|
|
|
|
|
|
|
|
|
|
class TriggerSensorEntity(TriggerEntity, SensorEntity):
|
|
|
|
"""Sensor entity based on trigger data."""
|
|
|
|
|
|
|
|
extra_template_keys = (CONF_VALUE_TEMPLATE,)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def state(self) -> str | None:
|
|
|
|
"""Return state of the sensor."""
|
|
|
|
return self._rendered.get(CONF_VALUE_TEMPLATE)
|