From 02389960ce62342fdfe7a213895c5e28dd92466b Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 13 Mar 2023 17:23:25 +0100 Subject: [PATCH] Refactor Command line sensor to inherit TemplateSensor (#81222) * Refactor sensor * Remove not needed * block until done * reset test * test sensor * Add time --- .../components/command_line/sensor.py | 68 ++++++++++++++----- tests/components/command_line/test_sensor.py | 22 +++++- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/command_line/sensor.py b/homeassistant/components/command_line/sensor.py index 5dbbbf88e58..24224c12cac 100644 --- a/homeassistant/components/command_line/sensor.py +++ b/homeassistant/components/command_line/sensor.py @@ -8,9 +8,16 @@ import logging import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.components.sensor import ( + CONF_STATE_CLASS, + DEVICE_CLASSES_SCHEMA, + PLATFORM_SCHEMA, + STATE_CLASSES_SCHEMA, + SensorEntity, +) from homeassistant.const import ( CONF_COMMAND, + CONF_DEVICE_CLASS, CONF_NAME, CONF_UNIQUE_ID, CONF_UNIT_OF_MEASUREMENT, @@ -21,8 +28,12 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import setup_reload_service +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.template import Template +from homeassistant.helpers.template_entity import ( + TEMPLATE_SENSOR_BASE_SCHEMA, + TemplateSensor, +) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import check_output_or_log @@ -45,19 +56,25 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, } ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Command Sensor.""" - setup_reload_service(hass, DOMAIN, PLATFORMS) + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) + + sensor_config = vol.Schema( + TEMPLATE_SENSOR_BASE_SCHEMA.schema, extra=vol.REMOVE_EXTRA + )(config) name: str = config[CONF_NAME] command: str = config[CONF_COMMAND] @@ -70,17 +87,30 @@ def setup_platform( json_attributes: list[str] | None = config.get(CONF_JSON_ATTRIBUTES) data = CommandSensorData(hass, command, command_timeout) - add_entities( - [CommandSensor(data, name, unit, value_template, json_attributes, unique_id)], + async_add_entities( + [ + CommandSensor( + hass, + sensor_config, + data, + name, + unit, + value_template, + json_attributes, + unique_id, + ) + ], True, ) -class CommandSensor(SensorEntity): +class CommandSensor(TemplateSensor, SensorEntity): """Representation of a sensor that is using shell commands.""" def __init__( self, + hass: HomeAssistant, + config: ConfigType, data: CommandSensorData, name: str, unit_of_measurement: str | None, @@ -89,18 +119,22 @@ class CommandSensor(SensorEntity): unique_id: str | None, ) -> None: """Initialize the sensor.""" + TemplateSensor.__init__( + self, + hass, + config=config, + fallback_name=name, + unique_id=unique_id, + ) self.data = data self._attr_extra_state_attributes = {} self._json_attributes = json_attributes - self._attr_name = name self._attr_native_value = None - self._attr_native_unit_of_measurement = unit_of_measurement self._value_template = value_template - self._attr_unique_id = unique_id - def update(self) -> None: + async def async_update(self) -> None: """Get the latest data and updates the state.""" - self.data.update() + await self.hass.async_add_executor_job(self.data.update) value = self.data.value if self._json_attributes: @@ -124,10 +158,10 @@ class CommandSensor(SensorEntity): if value is None: value = STATE_UNKNOWN elif self._value_template is not None: - self._attr_native_value = ( - self._value_template.render_with_possible_json_value( - value, STATE_UNKNOWN - ) + self._attr_native_value = await self.hass.async_add_executor_job( + self._value_template.render_with_possible_json_value, + value, + STATE_UNKNOWN, ) else: self._attr_native_value = value diff --git a/tests/components/command_line/test_sensor.py b/tests/components/command_line/test_sensor.py index f7de3b33944..5aab14225f1 100644 --- a/tests/components/command_line/test_sensor.py +++ b/tests/components/command_line/test_sensor.py @@ -1,6 +1,7 @@ """The tests for the Command line sensor platform.""" from __future__ import annotations +from datetime import timedelta from typing import Any from unittest.mock import patch @@ -10,6 +11,9 @@ from homeassistant import setup from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt + +from tests.common import async_fire_time_changed async def setup_test_entities(hass: HomeAssistant, config_dict: dict[str, Any]) -> None: @@ -67,6 +71,14 @@ async def test_template_render(hass: HomeAssistant) -> None: "command": "echo {{ states.sensor.input_sensor.state }}", }, ) + + # Give time for template to load + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=1), + ) + await hass.async_block_till_done() + entity_state = hass.states.get("sensor.test") assert entity_state assert entity_state.state == "sensor_value" @@ -86,7 +98,15 @@ async def test_template_render_with_quote(hass: HomeAssistant) -> None: }, ) - check_output.assert_called_once_with( + # Give time for template to load + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=1), + ) + await hass.async_block_till_done() + + assert len(check_output.mock_calls) == 2 + check_output.assert_called_with( 'echo "sensor_value" "3 4"', shell=True, # nosec # shell by design timeout=15,