mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 02:07:54 +00:00
Add json_attributes_path configuration for command_line sensor (#116656)
Add json attributes path config to command line sensor
This commit is contained in:
parent
490dd53edf
commit
ec536bda3d
@ -60,12 +60,17 @@ from homeassistant.helpers.service import async_register_admin_service
|
|||||||
from homeassistant.helpers.trigger_template_entity import CONF_AVAILABILITY
|
from homeassistant.helpers.trigger_template_entity import CONF_AVAILABILITY
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN
|
from .const import (
|
||||||
|
CONF_COMMAND_TIMEOUT,
|
||||||
|
CONF_JSON_ATTRIBUTES,
|
||||||
|
CONF_JSON_ATTRIBUTES_PATH,
|
||||||
|
DEFAULT_TIMEOUT,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
BINARY_SENSOR_DEFAULT_NAME = "Binary Command Sensor"
|
BINARY_SENSOR_DEFAULT_NAME = "Binary Command Sensor"
|
||||||
DEFAULT_PAYLOAD_ON = "ON"
|
DEFAULT_PAYLOAD_ON = "ON"
|
||||||
DEFAULT_PAYLOAD_OFF = "OFF"
|
DEFAULT_PAYLOAD_OFF = "OFF"
|
||||||
CONF_JSON_ATTRIBUTES = "json_attributes"
|
|
||||||
SENSOR_DEFAULT_NAME = "Command Sensor"
|
SENSOR_DEFAULT_NAME = "Command Sensor"
|
||||||
CONF_NOTIFIERS = "notifiers"
|
CONF_NOTIFIERS = "notifiers"
|
||||||
|
|
||||||
@ -126,6 +131,7 @@ SENSOR_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_COMMAND): cv.string,
|
vol.Required(CONF_COMMAND): cv.string,
|
||||||
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
|
vol.Optional(CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
|
||||||
vol.Optional(CONF_JSON_ATTRIBUTES): cv.ensure_list_csv,
|
vol.Optional(CONF_JSON_ATTRIBUTES): cv.ensure_list_csv,
|
||||||
|
vol.Optional(CONF_JSON_ATTRIBUTES_PATH): cv.string,
|
||||||
vol.Optional(CONF_NAME, default=SENSOR_DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=SENSOR_DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_ICON): cv.template,
|
vol.Optional(CONF_ICON): cv.template,
|
||||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||||
|
@ -18,6 +18,8 @@ from homeassistant.helpers.trigger_template_entity import (
|
|||||||
LOGGER = logging.getLogger(__package__)
|
LOGGER = logging.getLogger(__package__)
|
||||||
|
|
||||||
CONF_COMMAND_TIMEOUT = "command_timeout"
|
CONF_COMMAND_TIMEOUT = "command_timeout"
|
||||||
|
CONF_JSON_ATTRIBUTES = "json_attributes"
|
||||||
|
CONF_JSON_ATTRIBUTES_PATH = "json_attributes_path"
|
||||||
DEFAULT_TIMEOUT = 15
|
DEFAULT_TIMEOUT = 15
|
||||||
DOMAIN = "command_line"
|
DOMAIN = "command_line"
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
"name": "Command Line",
|
"name": "Command Line",
|
||||||
"codeowners": ["@gjohansson-ST"],
|
"codeowners": ["@gjohansson-ST"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/command_line",
|
"documentation": "https://www.home-assistant.io/integrations/command_line",
|
||||||
"iot_class": "local_polling"
|
"iot_class": "local_polling",
|
||||||
|
"requirements": ["jsonpath==0.82.2"]
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ from datetime import datetime, timedelta
|
|||||||
import json
|
import json
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
|
from jsonpath import jsonpath
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass
|
from homeassistant.components.sensor import SensorDeviceClass
|
||||||
from homeassistant.components.sensor.helpers import async_parse_date_datetime
|
from homeassistant.components.sensor.helpers import async_parse_date_datetime
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -25,11 +27,15 @@ from homeassistant.helpers.trigger_template_entity import ManualTriggerSensorEnt
|
|||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .const import CONF_COMMAND_TIMEOUT, LOGGER, TRIGGER_ENTITY_OPTIONS
|
from .const import (
|
||||||
|
CONF_COMMAND_TIMEOUT,
|
||||||
|
CONF_JSON_ATTRIBUTES,
|
||||||
|
CONF_JSON_ATTRIBUTES_PATH,
|
||||||
|
LOGGER,
|
||||||
|
TRIGGER_ENTITY_OPTIONS,
|
||||||
|
)
|
||||||
from .utils import async_check_output_or_log
|
from .utils import async_check_output_or_log
|
||||||
|
|
||||||
CONF_JSON_ATTRIBUTES = "json_attributes"
|
|
||||||
|
|
||||||
DEFAULT_NAME = "Command Sensor"
|
DEFAULT_NAME = "Command Sensor"
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=60)
|
SCAN_INTERVAL = timedelta(seconds=60)
|
||||||
@ -49,6 +55,7 @@ async def async_setup_platform(
|
|||||||
command: str = sensor_config[CONF_COMMAND]
|
command: str = sensor_config[CONF_COMMAND]
|
||||||
command_timeout: int = sensor_config[CONF_COMMAND_TIMEOUT]
|
command_timeout: int = sensor_config[CONF_COMMAND_TIMEOUT]
|
||||||
json_attributes: list[str] | None = sensor_config.get(CONF_JSON_ATTRIBUTES)
|
json_attributes: list[str] | None = sensor_config.get(CONF_JSON_ATTRIBUTES)
|
||||||
|
json_attributes_path: str | None = sensor_config.get(CONF_JSON_ATTRIBUTES_PATH)
|
||||||
scan_interval: timedelta = sensor_config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
|
scan_interval: timedelta = sensor_config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
|
||||||
data = CommandSensorData(hass, command, command_timeout)
|
data = CommandSensorData(hass, command, command_timeout)
|
||||||
|
|
||||||
@ -67,6 +74,7 @@ async def async_setup_platform(
|
|||||||
trigger_entity_config,
|
trigger_entity_config,
|
||||||
value_template,
|
value_template,
|
||||||
json_attributes,
|
json_attributes,
|
||||||
|
json_attributes_path,
|
||||||
scan_interval,
|
scan_interval,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@ -84,6 +92,7 @@ class CommandSensor(ManualTriggerSensorEntity):
|
|||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
value_template: Template | None,
|
value_template: Template | None,
|
||||||
json_attributes: list[str] | None,
|
json_attributes: list[str] | None,
|
||||||
|
json_attributes_path: str | None,
|
||||||
scan_interval: timedelta,
|
scan_interval: timedelta,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
@ -91,6 +100,7 @@ class CommandSensor(ManualTriggerSensorEntity):
|
|||||||
self.data = data
|
self.data = data
|
||||||
self._attr_extra_state_attributes: dict[str, Any] = {}
|
self._attr_extra_state_attributes: dict[str, Any] = {}
|
||||||
self._json_attributes = json_attributes
|
self._json_attributes = json_attributes
|
||||||
|
self._json_attributes_path = json_attributes_path
|
||||||
self._attr_native_value = None
|
self._attr_native_value = None
|
||||||
self._value_template = value_template
|
self._value_template = value_template
|
||||||
self._scan_interval = scan_interval
|
self._scan_interval = scan_interval
|
||||||
@ -141,6 +151,13 @@ class CommandSensor(ManualTriggerSensorEntity):
|
|||||||
if value:
|
if value:
|
||||||
try:
|
try:
|
||||||
json_dict = json.loads(value)
|
json_dict = json.loads(value)
|
||||||
|
if self._json_attributes_path is not None:
|
||||||
|
json_dict = jsonpath(json_dict, self._json_attributes_path)
|
||||||
|
# jsonpath will always store the result in json_dict[0]
|
||||||
|
# so the next line happens to work exactly as needed to
|
||||||
|
# find the result
|
||||||
|
if isinstance(json_dict, list):
|
||||||
|
json_dict = json_dict[0]
|
||||||
if isinstance(json_dict, Mapping):
|
if isinstance(json_dict, Mapping):
|
||||||
self._attr_extra_state_attributes = {
|
self._attr_extra_state_attributes = {
|
||||||
k: json_dict[k]
|
k: json_dict[k]
|
||||||
|
@ -1187,6 +1187,7 @@ jaraco.abode==5.1.2
|
|||||||
# homeassistant.components.jellyfin
|
# homeassistant.components.jellyfin
|
||||||
jellyfin-apiclient-python==1.9.2
|
jellyfin-apiclient-python==1.9.2
|
||||||
|
|
||||||
|
# homeassistant.components.command_line
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
jsonpath==0.82.2
|
jsonpath==0.82.2
|
||||||
|
|
||||||
|
@ -974,6 +974,7 @@ jaraco.abode==5.1.2
|
|||||||
# homeassistant.components.jellyfin
|
# homeassistant.components.jellyfin
|
||||||
jellyfin-apiclient-python==1.9.2
|
jellyfin-apiclient-python==1.9.2
|
||||||
|
|
||||||
|
# homeassistant.components.command_line
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
jsonpath==0.82.2
|
jsonpath==0.82.2
|
||||||
|
|
||||||
|
@ -467,6 +467,46 @@ async def test_update_with_unnecessary_json_attrs(
|
|||||||
assert "key_three" not in entity_state.attributes
|
assert "key_three" not in entity_state.attributes
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"get_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"command_line": [
|
||||||
|
{
|
||||||
|
"sensor": {
|
||||||
|
"name": "Test",
|
||||||
|
"command": 'echo \
|
||||||
|
{\
|
||||||
|
\\"top_level\\": {\
|
||||||
|
\\"second_level\\": {\
|
||||||
|
\\"key\\": \\"some_json_value\\",\
|
||||||
|
\\"another_key\\": \\"another_json_value\\",\
|
||||||
|
\\"key_three\\": \\"value_three\\"\
|
||||||
|
}\
|
||||||
|
}\
|
||||||
|
}',
|
||||||
|
"json_attributes": ["key", "another_key", "key_three"],
|
||||||
|
"json_attributes_path": "$.top_level.second_level",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_update_with_json_attrs_with_json_attrs_path(
|
||||||
|
hass: HomeAssistant, load_yaml_integration: None
|
||||||
|
) -> None:
|
||||||
|
"""Test using json_attributes_path to select a different part of the json object as root."""
|
||||||
|
|
||||||
|
entity_state = hass.states.get("sensor.test")
|
||||||
|
assert entity_state
|
||||||
|
assert entity_state.attributes["key"] == "some_json_value"
|
||||||
|
assert entity_state.attributes["another_key"] == "another_json_value"
|
||||||
|
assert entity_state.attributes["key_three"] == "value_three"
|
||||||
|
assert "top_level" not in entity_state.attributes
|
||||||
|
assert "second_level" not in entity_state.attributes
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"get_config",
|
"get_config",
|
||||||
[
|
[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user