Command Line Sensor - json_attributes (#15679)
* Add tests to command_line for json_attrs * Add json_attrs to command_line * Remove whitespace on blank line * Stick to <80 row length * Use collections.Mapping, not dict * Rename *attrs to *attributes * Remove extraneous + for string concat * Test multiple keys * Add test Makes sure the sensor's attributes don't contain a value for a missing key, even if we want that key. * Test that unwanted keys are skipped * Remove additional log line * Update tests for log changes * Fix ordering
This commit is contained in:
parent
a2b793c61b
commit
1d68f4e279
2 changed files with 153 additions and 16 deletions
|
@ -4,39 +4,42 @@ Allows to configure custom shell commands to turn a value for a sensor.
|
|||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.command_line/
|
||||
"""
|
||||
import logging
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
import collections
|
||||
from datetime import timedelta
|
||||
import json
|
||||
import logging
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.helpers import template
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_VALUE_TEMPLATE, CONF_UNIT_OF_MEASUREMENT, CONF_COMMAND,
|
||||
CONF_COMMAND, CONF_NAME, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE,
|
||||
STATE_UNKNOWN)
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_COMMAND_TIMEOUT = 'command_timeout'
|
||||
CONF_JSON_ATTRIBUTES = 'json_attributes'
|
||||
|
||||
DEFAULT_NAME = 'Command Sensor'
|
||||
DEFAULT_TIMEOUT = 15
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
CONF_COMMAND_TIMEOUT = 'command_timeout'
|
||||
DEFAULT_TIMEOUT = 15
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_COMMAND): cv.string,
|
||||
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT):
|
||||
cv.positive_int,
|
||||
vol.Optional(CONF_JSON_ATTRIBUTES): cv.ensure_list_csv,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(
|
||||
CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
|
||||
})
|
||||
|
||||
|
||||
|
@ -49,18 +52,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
command_timeout = config.get(CONF_COMMAND_TIMEOUT)
|
||||
if value_template is not None:
|
||||
value_template.hass = hass
|
||||
json_attributes = config.get(CONF_JSON_ATTRIBUTES)
|
||||
data = CommandSensorData(hass, command, command_timeout)
|
||||
|
||||
add_devices([CommandSensor(hass, data, name, unit, value_template)], True)
|
||||
add_devices([CommandSensor(
|
||||
hass, data, name, unit, value_template, json_attributes)], True)
|
||||
|
||||
|
||||
class CommandSensor(Entity):
|
||||
"""Representation of a sensor that is using shell commands."""
|
||||
|
||||
def __init__(self, hass, data, name, unit_of_measurement, value_template):
|
||||
def __init__(self, hass, data, name, unit_of_measurement, value_template,
|
||||
json_attributes):
|
||||
"""Initialize the sensor."""
|
||||
self._hass = hass
|
||||
self.data = data
|
||||
self._attributes = None
|
||||
self._json_attributes = json_attributes
|
||||
self._name = name
|
||||
self._state = None
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
|
@ -81,11 +89,33 @@ class CommandSensor(Entity):
|
|||
"""Return the state of the device."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return self._attributes
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the state."""
|
||||
self.data.update()
|
||||
value = self.data.value
|
||||
|
||||
if self._json_attributes:
|
||||
self._attributes = {}
|
||||
if value:
|
||||
try:
|
||||
json_dict = json.loads(value)
|
||||
if isinstance(json_dict, collections.Mapping):
|
||||
self._attributes = {k: json_dict[k] for k in
|
||||
self._json_attributes
|
||||
if k in json_dict}
|
||||
else:
|
||||
_LOGGER.warning("JSON result was not a dictionary")
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
"Unable to parse output as JSON: %s", value)
|
||||
else:
|
||||
_LOGGER.warning("Empty reply found when expecting JSON data")
|
||||
|
||||
if value is None:
|
||||
value = STATE_UNKNOWN
|
||||
elif self._value_template is not None:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue