mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Add availability to command_line (#105300)
* Add availability to command_line * Add tests * freezer
This commit is contained in:
parent
6a9fdaae7a
commit
749ef45727
@ -55,6 +55,7 @@ import homeassistant.helpers.config_validation as cv
|
|||||||
from homeassistant.helpers.entity_platform import async_get_platforms
|
from homeassistant.helpers.entity_platform import async_get_platforms
|
||||||
from homeassistant.helpers.reload import async_integration_yaml_config
|
from homeassistant.helpers.reload import async_integration_yaml_config
|
||||||
from homeassistant.helpers.service import async_register_admin_service
|
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 homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN
|
from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN
|
||||||
@ -90,6 +91,7 @@ BINARY_SENSOR_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_SCAN_INTERVAL, default=BINARY_SENSOR_DEFAULT_SCAN_INTERVAL
|
CONF_SCAN_INTERVAL, default=BINARY_SENSOR_DEFAULT_SCAN_INTERVAL
|
||||||
): vol.All(cv.time_period, cv.positive_timedelta),
|
): vol.All(cv.time_period, cv.positive_timedelta),
|
||||||
|
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
COVER_SCHEMA = vol.Schema(
|
COVER_SCHEMA = vol.Schema(
|
||||||
@ -105,6 +107,7 @@ COVER_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(CONF_SCAN_INTERVAL, default=COVER_DEFAULT_SCAN_INTERVAL): vol.All(
|
vol.Optional(CONF_SCAN_INTERVAL, default=COVER_DEFAULT_SCAN_INTERVAL): vol.All(
|
||||||
cv.time_period, cv.positive_timedelta
|
cv.time_period, cv.positive_timedelta
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
NOTIFY_SCHEMA = vol.Schema(
|
NOTIFY_SCHEMA = vol.Schema(
|
||||||
@ -129,6 +132,7 @@ SENSOR_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(CONF_SCAN_INTERVAL, default=SENSOR_DEFAULT_SCAN_INTERVAL): vol.All(
|
vol.Optional(CONF_SCAN_INTERVAL, default=SENSOR_DEFAULT_SCAN_INTERVAL): vol.All(
|
||||||
cv.time_period, cv.positive_timedelta
|
cv.time_period, cv.positive_timedelta
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
SWITCH_SCHEMA = vol.Schema(
|
SWITCH_SCHEMA = vol.Schema(
|
||||||
@ -144,6 +148,7 @@ SWITCH_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(CONF_SCAN_INTERVAL, default=SWITCH_DEFAULT_SCAN_INTERVAL): vol.All(
|
vol.Optional(CONF_SCAN_INTERVAL, default=SWITCH_DEFAULT_SCAN_INTERVAL): vol.All(
|
||||||
cv.time_period, cv.positive_timedelta
|
cv.time_period, cv.positive_timedelta
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
COMBINED_SCHEMA = vol.Schema(
|
COMBINED_SCHEMA = vol.Schema(
|
||||||
|
@ -24,7 +24,10 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.template import Template
|
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.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
@ -63,6 +66,7 @@ async def async_setup_platform(
|
|||||||
scan_interval: timedelta = binary_sensor_config.get(
|
scan_interval: timedelta = binary_sensor_config.get(
|
||||||
CONF_SCAN_INTERVAL, SCAN_INTERVAL
|
CONF_SCAN_INTERVAL, SCAN_INTERVAL
|
||||||
)
|
)
|
||||||
|
availability: Template | None = binary_sensor_config.get(CONF_AVAILABILITY)
|
||||||
if value_template is not None:
|
if value_template is not None:
|
||||||
value_template.hass = hass
|
value_template.hass = hass
|
||||||
data = CommandSensorData(hass, command, command_timeout)
|
data = CommandSensorData(hass, command, command_timeout)
|
||||||
@ -72,6 +76,7 @@ async def async_setup_platform(
|
|||||||
CONF_NAME: Template(name, hass),
|
CONF_NAME: Template(name, hass),
|
||||||
CONF_DEVICE_CLASS: device_class,
|
CONF_DEVICE_CLASS: device_class,
|
||||||
CONF_ICON: icon,
|
CONF_ICON: icon,
|
||||||
|
CONF_AVAILABILITY: availability,
|
||||||
}
|
}
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
|
@ -20,7 +20,10 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.template import Template
|
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.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import dt as dt_util, slugify
|
from homeassistant.util import dt as dt_util, slugify
|
||||||
|
|
||||||
@ -50,6 +53,7 @@ async def async_setup_platform(
|
|||||||
trigger_entity_config = {
|
trigger_entity_config = {
|
||||||
CONF_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
|
CONF_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
|
||||||
CONF_NAME: Template(device_config.get(CONF_NAME, device_name), hass),
|
CONF_NAME: Template(device_config.get(CONF_NAME, device_name), hass),
|
||||||
|
CONF_AVAILABILITY: device_config.get(CONF_AVAILABILITY),
|
||||||
}
|
}
|
||||||
|
|
||||||
covers.append(
|
covers.append(
|
||||||
|
@ -20,7 +20,10 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.template import Template
|
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.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import dt as dt_util, slugify
|
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_UNIQUE_ID: device_config.get(CONF_UNIQUE_ID),
|
||||||
CONF_NAME: Template(device_config.get(CONF_NAME, object_id), hass),
|
CONF_NAME: Template(device_config.get(CONF_NAME, object_id), hass),
|
||||||
CONF_ICON: device_config.get(CONF_ICON),
|
CONF_ICON: device_config.get(CONF_ICON),
|
||||||
|
CONF_AVAILABILITY: device_config.get(CONF_AVAILABILITY),
|
||||||
}
|
}
|
||||||
|
|
||||||
value_template: Template | None = device_config.get(CONF_VALUE_TEMPLATE)
|
value_template: Template | None = device_config.get(CONF_VALUE_TEMPLATE)
|
||||||
|
@ -6,6 +6,7 @@ from datetime import timedelta
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
@ -15,7 +16,7 @@ from homeassistant.components.homeassistant import (
|
|||||||
DOMAIN as HA_DOMAIN,
|
DOMAIN as HA_DOMAIN,
|
||||||
SERVICE_UPDATE_ENTITY,
|
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.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
@ -289,3 +290,53 @@ async def test_updating_manually(
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert called
|
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
|
||||||
|
@ -7,6 +7,7 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
@ -22,6 +23,8 @@ from homeassistant.const import (
|
|||||||
SERVICE_CLOSE_COVER,
|
SERVICE_CLOSE_COVER,
|
||||||
SERVICE_OPEN_COVER,
|
SERVICE_OPEN_COVER,
|
||||||
SERVICE_STOP_COVER,
|
SERVICE_STOP_COVER,
|
||||||
|
STATE_OPEN,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
@ -340,3 +343,50 @@ async def test_updating_manually(
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert called
|
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
|
||||||
|
@ -7,6 +7,7 @@ import subprocess
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
@ -16,7 +17,7 @@ from homeassistant.components.homeassistant import (
|
|||||||
DOMAIN as HA_DOMAIN,
|
DOMAIN as HA_DOMAIN,
|
||||||
SERVICE_UPDATE_ENTITY,
|
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.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.util import dt as dt_util
|
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"
|
"Template variable error: 'None' has no attribute 'split' when rendering"
|
||||||
not in caplog.text
|
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
|
||||||
|
@ -9,6 +9,7 @@ import subprocess
|
|||||||
import tempfile
|
import tempfile
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
@ -25,6 +26,7 @@ from homeassistant.const import (
|
|||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
@ -710,3 +712,52 @@ async def test_updating_manually(
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert called
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user