Add availability to command_line (#105300)

* Add availability to command_line

* Add tests

* freezer
This commit is contained in:
G Johansson 2024-01-15 18:20:34 +01:00 committed by GitHub
parent 6a9fdaae7a
commit 749ef45727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 225 additions and 5 deletions

View File

@ -55,6 +55,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.helpers.reload import async_integration_yaml_config
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.trigger_template_entity import CONF_AVAILABILITY
from homeassistant.helpers.typing import ConfigType
from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN
@ -90,6 +91,7 @@ BINARY_SENSOR_SCHEMA = vol.Schema(
vol.Optional(
CONF_SCAN_INTERVAL, default=BINARY_SENSOR_DEFAULT_SCAN_INTERVAL
): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_AVAILABILITY): cv.template,
}
)
COVER_SCHEMA = vol.Schema(
@ -105,6 +107,7 @@ COVER_SCHEMA = vol.Schema(
vol.Optional(CONF_SCAN_INTERVAL, default=COVER_DEFAULT_SCAN_INTERVAL): vol.All(
cv.time_period, cv.positive_timedelta
),
vol.Optional(CONF_AVAILABILITY): cv.template,
}
)
NOTIFY_SCHEMA = vol.Schema(
@ -129,6 +132,7 @@ SENSOR_SCHEMA = vol.Schema(
vol.Optional(CONF_SCAN_INTERVAL, default=SENSOR_DEFAULT_SCAN_INTERVAL): vol.All(
cv.time_period, cv.positive_timedelta
),
vol.Optional(CONF_AVAILABILITY): cv.template,
}
)
SWITCH_SCHEMA = vol.Schema(
@ -144,6 +148,7 @@ SWITCH_SCHEMA = vol.Schema(
vol.Optional(CONF_SCAN_INTERVAL, default=SWITCH_DEFAULT_SCAN_INTERVAL): vol.All(
cv.time_period, cv.positive_timedelta
),
vol.Optional(CONF_AVAILABILITY): cv.template,
}
)
COMBINED_SCHEMA = vol.Schema(

View File

@ -24,7 +24,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
CONF_AVAILABILITY,
ManualTriggerEntity,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
@ -63,6 +66,7 @@ async def async_setup_platform(
scan_interval: timedelta = binary_sensor_config.get(
CONF_SCAN_INTERVAL, SCAN_INTERVAL
)
availability: Template | None = binary_sensor_config.get(CONF_AVAILABILITY)
if value_template is not None:
value_template.hass = hass
data = CommandSensorData(hass, command, command_timeout)
@ -72,6 +76,7 @@ async def async_setup_platform(
CONF_NAME: Template(name, hass),
CONF_DEVICE_CLASS: device_class,
CONF_ICON: icon,
CONF_AVAILABILITY: availability,
}
async_add_entities(

View File

@ -20,7 +20,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
CONF_AVAILABILITY,
ManualTriggerEntity,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util, slugify
@ -50,6 +53,7 @@ async def async_setup_platform(
trigger_entity_config = {
CONF_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
CONF_NAME: Template(device_config.get(CONF_NAME, device_name), hass),
CONF_AVAILABILITY: device_config.get(CONF_AVAILABILITY),
}
covers.append(

View File

@ -20,7 +20,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.template import Template
from homeassistant.helpers.trigger_template_entity import ManualTriggerEntity
from homeassistant.helpers.trigger_template_entity import (
CONF_AVAILABILITY,
ManualTriggerEntity,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util, slugify
@ -48,6 +51,7 @@ async def async_setup_platform(
CONF_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
CONF_NAME: Template(device_config.get(CONF_NAME, object_id), hass),
CONF_ICON: device_config.get(CONF_ICON),
CONF_AVAILABILITY: device_config.get(CONF_AVAILABILITY),
}
value_template: Template | None = device_config.get(CONF_VALUE_TEMPLATE)

View File

@ -6,6 +6,7 @@ from datetime import timedelta
from typing import Any
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant import setup
@ -15,7 +16,7 @@ from homeassistant.components.homeassistant import (
DOMAIN as HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util
@ -289,3 +290,53 @@ async def test_updating_manually(
)
await hass.async_block_till_done()
assert called
@pytest.mark.parametrize(
"get_config",
[
{
"command_line": [
{
"binary_sensor": {
"name": "Test",
"command": "echo 10",
"payload_on": "1.0",
"payload_off": "0",
"value_template": "{{ value | multiply(0.1) }}",
"availability": '{{ states("sensor.input1")=="on" }}',
}
}
]
}
],
)
async def test_availability(
hass: HomeAssistant,
load_yaml_integration: None,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test availability."""
hass.states.async_set("sensor.input1", "on")
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("binary_sensor.test")
assert entity_state
assert entity_state.state == STATE_ON
hass.states.async_set("sensor.input1", "off")
await hass.async_block_till_done()
with patch(
"homeassistant.components.command_line.utils.subprocess.check_output",
return_value=b"0",
):
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("binary_sensor.test")
assert entity_state
assert entity_state.state == STATE_UNAVAILABLE

View File

@ -7,6 +7,7 @@ import os
import tempfile
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant import setup
@ -22,6 +23,8 @@ from homeassistant.const import (
SERVICE_CLOSE_COVER,
SERVICE_OPEN_COVER,
SERVICE_STOP_COVER,
STATE_OPEN,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@ -340,3 +343,50 @@ async def test_updating_manually(
)
await hass.async_block_till_done()
assert called
@pytest.mark.parametrize(
"get_config",
[
{
"command_line": [
{
"cover": {
"command_state": "echo 10",
"name": "Test",
"availability": '{{ states("sensor.input1")=="on" }}',
},
}
]
}
],
)
async def test_availability(
hass: HomeAssistant,
load_yaml_integration: None,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test availability."""
hass.states.async_set("sensor.input1", "on")
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("cover.test")
assert entity_state
assert entity_state.state == STATE_OPEN
hass.states.async_set("sensor.input1", "off")
await hass.async_block_till_done()
with patch(
"homeassistant.components.command_line.utils.subprocess.check_output",
return_value=b"50\n",
):
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("cover.test")
assert entity_state
assert entity_state.state == STATE_UNAVAILABLE

View File

@ -7,6 +7,7 @@ import subprocess
from typing import Any
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant import setup
@ -16,7 +17,7 @@ from homeassistant.components.homeassistant import (
DOMAIN as HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util
@ -708,3 +709,52 @@ async def test_template_not_error_when_data_is_none(
"Template variable error: 'None' has no attribute 'split' when rendering"
not in caplog.text
)
@pytest.mark.parametrize(
"get_config",
[
{
"command_line": [
{
"sensor": {
"name": "Test",
"command": "echo January 17, 2022",
"device_class": "date",
"value_template": "{{ strptime(value, '%B %d, %Y').strftime('%Y-%m-%d') }}",
"availability": '{{ states("sensor.input1")=="on" }}',
}
}
]
}
],
)
async def test_availability(
hass: HomeAssistant,
load_yaml_integration: None,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test availability."""
hass.states.async_set("sensor.input1", "on")
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("sensor.test")
assert entity_state
assert entity_state.state == "2022-01-17"
hass.states.async_set("sensor.input1", "off")
await hass.async_block_till_done()
with patch(
"homeassistant.components.command_line.utils.subprocess.check_output",
return_value=b"January 17, 2022",
):
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("sensor.test")
assert entity_state
assert entity_state.state == STATE_UNAVAILABLE

View File

@ -9,6 +9,7 @@ import subprocess
import tempfile
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant import setup
@ -25,6 +26,7 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@ -710,3 +712,52 @@ async def test_updating_manually(
)
await hass.async_block_till_done()
assert called
@pytest.mark.parametrize(
"get_config",
[
{
"command_line": [
{
"switch": {
"command_state": "echo 1",
"command_on": "echo 2",
"command_off": "echo 3",
"name": "Test",
"availability": '{{ states("sensor.input1")=="on" }}',
},
}
]
}
],
)
async def test_availability(
hass: HomeAssistant,
load_yaml_integration: None,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test availability."""
hass.states.async_set("sensor.input1", "on")
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("switch.test")
assert entity_state
assert entity_state.state == STATE_ON
hass.states.async_set("sensor.input1", "off")
await hass.async_block_till_done()
with patch(
"homeassistant.components.command_line.utils.subprocess.check_output",
return_value=b"50\n",
):
freezer.tick(timedelta(minutes=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_state = hass.states.get("switch.test")
assert entity_state
assert entity_state.state == STATE_UNAVAILABLE