mirror of
https://github.com/home-assistant/core.git
synced 2025-08-01 17:48:26 +00:00
Homekit valve duration characteristics (#149698)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
c72c600de4
commit
61396d92a5
@ -628,12 +628,12 @@ class HomeAccessory(Accessory): # type: ignore[misc]
|
||||
self,
|
||||
domain: str,
|
||||
service: str,
|
||||
service_data: dict[str, Any] | None,
|
||||
service_data: dict[str, Any],
|
||||
value: Any | None = None,
|
||||
) -> None:
|
||||
"""Fire event and call service for changes from HomeKit."""
|
||||
event_data = {
|
||||
ATTR_ENTITY_ID: self.entity_id,
|
||||
ATTR_ENTITY_ID: service_data.get(ATTR_ENTITY_ID, self.entity_id),
|
||||
ATTR_DISPLAY_NAME: self.display_name,
|
||||
ATTR_SERVICE: service,
|
||||
ATTR_VALUE: value,
|
||||
|
@ -57,6 +57,8 @@ CONF_LINKED_HUMIDITY_SENSOR = "linked_humidity_sensor"
|
||||
CONF_LINKED_OBSTRUCTION_SENSOR = "linked_obstruction_sensor"
|
||||
CONF_LINKED_PM25_SENSOR = "linked_pm25_sensor"
|
||||
CONF_LINKED_TEMPERATURE_SENSOR = "linked_temperature_sensor"
|
||||
CONF_LINKED_VALVE_DURATION = "linked_valve_duration"
|
||||
CONF_LINKED_VALVE_END_TIME = "linked_valve_end_time"
|
||||
CONF_LOW_BATTERY_THRESHOLD = "low_battery_threshold"
|
||||
CONF_MAX_FPS = "max_fps"
|
||||
CONF_MAX_HEIGHT = "max_height"
|
||||
@ -229,10 +231,12 @@ CHAR_ON = "On"
|
||||
CHAR_OUTLET_IN_USE = "OutletInUse"
|
||||
CHAR_POSITION_STATE = "PositionState"
|
||||
CHAR_PROGRAMMABLE_SWITCH_EVENT = "ProgrammableSwitchEvent"
|
||||
CHAR_REMAINING_DURATION = "RemainingDuration"
|
||||
CHAR_REMOTE_KEY = "RemoteKey"
|
||||
CHAR_ROTATION_DIRECTION = "RotationDirection"
|
||||
CHAR_ROTATION_SPEED = "RotationSpeed"
|
||||
CHAR_SATURATION = "Saturation"
|
||||
CHAR_SET_DURATION = "SetDuration"
|
||||
CHAR_SERIAL_NUMBER = "SerialNumber"
|
||||
CHAR_SERVICE_LABEL_INDEX = "ServiceLabelIndex"
|
||||
CHAR_SERVICE_LABEL_NAMESPACE = "ServiceLabelNamespace"
|
||||
|
@ -15,6 +15,11 @@ from pyhap.const import (
|
||||
)
|
||||
|
||||
from homeassistant.components import button, input_button
|
||||
from homeassistant.components.input_number import (
|
||||
ATTR_VALUE as INPUT_NUMBER_ATTR_VALUE,
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE as INPUT_NUMBER_SERVICE_SET_VALUE,
|
||||
)
|
||||
from homeassistant.components.input_select import ATTR_OPTIONS, SERVICE_SELECT_OPTION
|
||||
from homeassistant.components.lawn_mower import (
|
||||
DOMAIN as LAWN_MOWER_DOMAIN,
|
||||
@ -45,6 +50,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .accessories import TYPES, HomeAccessory, HomeDriver
|
||||
from .const import (
|
||||
@ -54,7 +60,11 @@ from .const import (
|
||||
CHAR_NAME,
|
||||
CHAR_ON,
|
||||
CHAR_OUTLET_IN_USE,
|
||||
CHAR_REMAINING_DURATION,
|
||||
CHAR_SET_DURATION,
|
||||
CHAR_VALVE_TYPE,
|
||||
CONF_LINKED_VALVE_DURATION,
|
||||
CONF_LINKED_VALVE_END_TIME,
|
||||
SERV_OUTLET,
|
||||
SERV_SWITCH,
|
||||
SERV_VALVE,
|
||||
@ -271,7 +281,21 @@ class ValveBase(HomeAccessory):
|
||||
self.on_service = on_service
|
||||
self.off_service = off_service
|
||||
|
||||
serv_valve = self.add_preload_service(SERV_VALVE)
|
||||
self.chars = []
|
||||
|
||||
self.linked_duration_entity: str | None = self.config.get(
|
||||
CONF_LINKED_VALVE_DURATION
|
||||
)
|
||||
self.linked_end_time_entity: str | None = self.config.get(
|
||||
CONF_LINKED_VALVE_END_TIME
|
||||
)
|
||||
|
||||
if self.linked_duration_entity:
|
||||
self.chars.append(CHAR_SET_DURATION)
|
||||
if self.linked_end_time_entity:
|
||||
self.chars.append(CHAR_REMAINING_DURATION)
|
||||
|
||||
serv_valve = self.add_preload_service(SERV_VALVE, self.chars)
|
||||
self.char_active = serv_valve.configure_char(
|
||||
CHAR_ACTIVE, value=False, setter_callback=self.set_state
|
||||
)
|
||||
@ -279,6 +303,25 @@ class ValveBase(HomeAccessory):
|
||||
self.char_valve_type = serv_valve.configure_char(
|
||||
CHAR_VALVE_TYPE, value=VALVE_TYPE[valve_type].valve_type
|
||||
)
|
||||
|
||||
if CHAR_SET_DURATION in self.chars:
|
||||
_LOGGER.debug(
|
||||
"%s: Add characteristic %s", self.entity_id, CHAR_SET_DURATION
|
||||
)
|
||||
self.char_set_duration = serv_valve.configure_char(
|
||||
CHAR_SET_DURATION,
|
||||
value=self.get_duration(),
|
||||
setter_callback=self.set_duration,
|
||||
)
|
||||
|
||||
if CHAR_REMAINING_DURATION in self.chars:
|
||||
_LOGGER.debug(
|
||||
"%s: Add characteristic %s", self.entity_id, CHAR_REMAINING_DURATION
|
||||
)
|
||||
self.char_remaining_duration = serv_valve.configure_char(
|
||||
CHAR_REMAINING_DURATION, getter_callback=self.get_remaining_duration
|
||||
)
|
||||
|
||||
# Set the state so it is in sync on initial
|
||||
# GET to avoid an event storm after homekit startup
|
||||
self.async_update_state(state)
|
||||
@ -294,12 +337,75 @@ class ValveBase(HomeAccessory):
|
||||
@callback
|
||||
def async_update_state(self, new_state: State) -> None:
|
||||
"""Update switch state after state changed."""
|
||||
self._update_duration_chars()
|
||||
current_state = 1 if new_state.state in self.open_states else 0
|
||||
_LOGGER.debug("%s: Set active state to %s", self.entity_id, current_state)
|
||||
self.char_active.set_value(current_state)
|
||||
_LOGGER.debug("%s: Set in_use state to %s", self.entity_id, current_state)
|
||||
self.char_in_use.set_value(current_state)
|
||||
|
||||
def _update_duration_chars(self) -> None:
|
||||
"""Update valve duration related properties if characteristics are available."""
|
||||
if CHAR_SET_DURATION in self.chars:
|
||||
self.char_set_duration.set_value(self.get_duration())
|
||||
if CHAR_REMAINING_DURATION in self.chars:
|
||||
self.char_remaining_duration.set_value(self.get_remaining_duration())
|
||||
|
||||
def set_duration(self, value: int) -> None:
|
||||
"""Set default duration for how long the valve should remain open."""
|
||||
_LOGGER.debug("%s: Set default run time to %s", self.entity_id, value)
|
||||
self.async_call_service(
|
||||
INPUT_NUMBER_DOMAIN,
|
||||
INPUT_NUMBER_SERVICE_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: self.linked_duration_entity,
|
||||
INPUT_NUMBER_ATTR_VALUE: value,
|
||||
},
|
||||
value,
|
||||
)
|
||||
|
||||
def get_duration(self) -> int:
|
||||
"""Get the default duration from Home Assistant."""
|
||||
duration_state = self._get_entity_state(self.linked_duration_entity)
|
||||
if duration_state is None:
|
||||
_LOGGER.debug(
|
||||
"%s: No linked duration entity state available", self.entity_id
|
||||
)
|
||||
return 0
|
||||
|
||||
try:
|
||||
duration = float(duration_state)
|
||||
return max(int(duration), 0)
|
||||
except ValueError:
|
||||
_LOGGER.debug("%s: Cannot parse linked duration entity", self.entity_id)
|
||||
return 0
|
||||
|
||||
def get_remaining_duration(self) -> int:
|
||||
"""Calculate the remaining duration based on end time in Home Assistant."""
|
||||
end_time_state = self._get_entity_state(self.linked_end_time_entity)
|
||||
if end_time_state is None:
|
||||
_LOGGER.debug(
|
||||
"%s: No linked end time entity state available", self.entity_id
|
||||
)
|
||||
return self.get_duration()
|
||||
|
||||
end_time = dt_util.parse_datetime(end_time_state)
|
||||
if end_time is None:
|
||||
_LOGGER.debug("%s: Cannot parse linked end time entity", self.entity_id)
|
||||
return self.get_duration()
|
||||
|
||||
remaining_time = (end_time - dt_util.utcnow()).total_seconds()
|
||||
return max(int(remaining_time), 0)
|
||||
|
||||
def _get_entity_state(self, entity_id: str | None) -> str | None:
|
||||
"""Fetch the state of a linked entity."""
|
||||
if entity_id is None:
|
||||
return None
|
||||
state = self.hass.states.get(entity_id)
|
||||
if state is None:
|
||||
return None
|
||||
return state.state
|
||||
|
||||
|
||||
@TYPES.register("ValveSwitch")
|
||||
class ValveSwitch(ValveBase):
|
||||
|
@ -17,6 +17,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import (
|
||||
binary_sensor,
|
||||
input_number,
|
||||
media_player,
|
||||
persistent_notification,
|
||||
sensor,
|
||||
@ -69,6 +70,8 @@ from .const import (
|
||||
CONF_LINKED_OBSTRUCTION_SENSOR,
|
||||
CONF_LINKED_PM25_SENSOR,
|
||||
CONF_LINKED_TEMPERATURE_SENSOR,
|
||||
CONF_LINKED_VALVE_DURATION,
|
||||
CONF_LINKED_VALVE_END_TIME,
|
||||
CONF_LOW_BATTERY_THRESHOLD,
|
||||
CONF_MAX_FPS,
|
||||
CONF_MAX_HEIGHT,
|
||||
@ -266,7 +269,9 @@ SWITCH_TYPE_SCHEMA = BASIC_INFO_SCHEMA.extend(
|
||||
TYPE_VALVE,
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_LINKED_VALVE_DURATION): cv.entity_domain(input_number.DOMAIN),
|
||||
vol.Optional(CONF_LINKED_VALVE_END_TIME): cv.entity_domain(sensor.DOMAIN),
|
||||
}
|
||||
)
|
||||
|
||||
@ -277,6 +282,12 @@ SENSOR_SCHEMA = BASIC_INFO_SCHEMA.extend(
|
||||
}
|
||||
)
|
||||
|
||||
VALVE_SCHEMA = BASIC_INFO_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_LINKED_VALVE_DURATION): cv.entity_domain(input_number.DOMAIN),
|
||||
vol.Optional(CONF_LINKED_VALVE_END_TIME): cv.entity_domain(sensor.DOMAIN),
|
||||
}
|
||||
)
|
||||
|
||||
HOMEKIT_CHAR_TRANSLATIONS = {
|
||||
0: " ", # nul
|
||||
@ -360,6 +371,9 @@ def validate_entity_config(values: dict) -> dict[str, dict]:
|
||||
elif domain == "sensor":
|
||||
config = SENSOR_SCHEMA(config)
|
||||
|
||||
elif domain == "valve":
|
||||
config = VALVE_SCHEMA(config)
|
||||
|
||||
else:
|
||||
config = BASIC_INFO_SCHEMA(config)
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homekit.const import (
|
||||
@ -22,6 +23,10 @@ from homeassistant.components.homekit.type_switches import (
|
||||
Valve,
|
||||
ValveSwitch,
|
||||
)
|
||||
from homeassistant.components.input_number import (
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE as INPUT_NUMBER_SERVICE_SET_VALUE,
|
||||
)
|
||||
from homeassistant.components.lawn_mower import (
|
||||
DOMAIN as LAWN_MOWER_DOMAIN,
|
||||
SERVICE_DOCK,
|
||||
@ -30,6 +35,7 @@ from homeassistant.components.lawn_mower import (
|
||||
LawnMowerEntityFeature,
|
||||
)
|
||||
from homeassistant.components.select import ATTR_OPTIONS
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.vacuum import (
|
||||
DOMAIN as VACUUM_DOMAIN,
|
||||
SERVICE_RETURN_TO_BASE,
|
||||
@ -658,3 +664,223 @@ async def test_button_switch(
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_on.value is False
|
||||
assert len(events) == 1
|
||||
|
||||
|
||||
async def test_valve_switch_with_set_duration_characteristic(
|
||||
hass: HomeAssistant, hk_driver, events: list[Event]
|
||||
) -> None:
|
||||
"""Test valve switch with set duration characteristic."""
|
||||
entity_id = "switch.sprinkler"
|
||||
|
||||
hass.states.async_set(entity_id, STATE_OFF)
|
||||
hass.states.async_set("input_number.valve_duration", "0")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock switch services to prevent errors
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_ON)
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_OFF)
|
||||
|
||||
acc = ValveSwitch(
|
||||
hass,
|
||||
hk_driver,
|
||||
"Sprinkler",
|
||||
entity_id,
|
||||
5,
|
||||
{"type": "sprinkler", "linked_valve_duration": "input_number.valve_duration"},
|
||||
)
|
||||
acc.run()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Assert initial state is synced
|
||||
assert acc.get_duration() == 0
|
||||
|
||||
# Simulate setting duration from HomeKit
|
||||
call_set_value = async_mock_service(
|
||||
hass, INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE
|
||||
)
|
||||
acc.char_set_duration.client_update_value(300)
|
||||
await hass.async_block_till_done()
|
||||
assert call_set_value
|
||||
assert call_set_value[0].data == {
|
||||
"entity_id": "input_number.valve_duration",
|
||||
"value": 300,
|
||||
}
|
||||
|
||||
# Assert state change in Home Assistant is synced to HomeKit
|
||||
hass.states.async_set("input_number.valve_duration", "600")
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 600
|
||||
|
||||
# Test fallback if no state is set
|
||||
hass.states.async_remove("input_number.valve_duration")
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 0
|
||||
|
||||
# Test remaining duration fallback if no end time is linked
|
||||
assert acc.get_remaining_duration() == 0
|
||||
|
||||
|
||||
async def test_valve_switch_with_remaining_duration_characteristic(
|
||||
hass: HomeAssistant, hk_driver, events: list[Event]
|
||||
) -> None:
|
||||
"""Test valve switch with remaining duration characteristic."""
|
||||
entity_id = "switch.sprinkler"
|
||||
|
||||
hass.states.async_set(entity_id, STATE_OFF)
|
||||
hass.states.async_set("sensor.valve_end_time", dt_util.utcnow().isoformat())
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock switch services to prevent errors
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_ON)
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_OFF)
|
||||
|
||||
acc = ValveSwitch(
|
||||
hass,
|
||||
hk_driver,
|
||||
"Sprinkler",
|
||||
entity_id,
|
||||
5,
|
||||
{"type": "sprinkler", "linked_valve_end_time": "sensor.valve_end_time"},
|
||||
)
|
||||
acc.run()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Assert initial state is synced
|
||||
assert acc.get_remaining_duration() == 0
|
||||
|
||||
# Simulate remaining duration update from Home Assistant
|
||||
with freeze_time(dt_util.utcnow()):
|
||||
hass.states.async_set(
|
||||
"sensor.valve_end_time",
|
||||
(dt_util.utcnow() + timedelta(seconds=90)).isoformat(),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Assert remaining duration is calculated correctly based on end time
|
||||
assert acc.get_remaining_duration() == 90
|
||||
|
||||
# Test fallback if no state is set
|
||||
hass.states.async_remove("sensor.valve_end_time")
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_remaining_duration() == 0
|
||||
|
||||
# Test get duration fallback if no duration is linked
|
||||
assert acc.get_duration() == 0
|
||||
|
||||
|
||||
async def test_valve_switch_with_duration_characteristics(
|
||||
hass: HomeAssistant, hk_driver, events: list[Event]
|
||||
) -> None:
|
||||
"""Test valve switch with set duration and remaining duration characteristics."""
|
||||
entity_id = "switch.sprinkler"
|
||||
|
||||
# Test with duration and end time entities linked
|
||||
hass.states.async_set(entity_id, STATE_OFF)
|
||||
hass.states.async_set("input_number.valve_duration", "300")
|
||||
hass.states.async_set("sensor.valve_end_time", dt_util.utcnow().isoformat())
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Mock switch services to prevent errors
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_ON)
|
||||
async_mock_service(hass, SWITCH_DOMAIN, SERVICE_TURN_OFF)
|
||||
# Mock input_number service for set_duration calls
|
||||
call_set_value = async_mock_service(
|
||||
hass, INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE
|
||||
)
|
||||
|
||||
acc = ValveSwitch(
|
||||
hass,
|
||||
hk_driver,
|
||||
"Sprinkler",
|
||||
entity_id,
|
||||
5,
|
||||
{
|
||||
"type": "sprinkler",
|
||||
"linked_valve_duration": "input_number.valve_duration",
|
||||
"linked_valve_end_time": "sensor.valve_end_time",
|
||||
},
|
||||
)
|
||||
acc.run()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Test update_duration_chars with both characteristics
|
||||
with freeze_time(dt_util.utcnow()):
|
||||
hass.states.async_set(
|
||||
"sensor.valve_end_time",
|
||||
(dt_util.utcnow() + timedelta(seconds=60)).isoformat(),
|
||||
)
|
||||
hass.states.async_set(entity_id, STATE_OFF)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.char_set_duration.value == 300
|
||||
assert acc.get_remaining_duration() == 60
|
||||
|
||||
# Test get_duration fallback with invalid state
|
||||
hass.states.async_set("input_number.valve_duration", "invalid")
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 0
|
||||
|
||||
# Test get_remaining_duration fallback with invalid state
|
||||
hass.states.async_set("sensor.valve_end_time", "invalid")
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_remaining_duration() == 0
|
||||
|
||||
# Test get_remaining_duration with end time in the past
|
||||
hass.states.async_set(
|
||||
"sensor.valve_end_time",
|
||||
(dt_util.utcnow() - timedelta(seconds=10)).isoformat(),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_remaining_duration() == 0
|
||||
|
||||
# Test set_duration with negative value
|
||||
acc.set_duration(-10)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 0
|
||||
# Verify the service was called with correct parameters
|
||||
assert len(call_set_value) == 1
|
||||
assert call_set_value[0].data == {
|
||||
"entity_id": "input_number.valve_duration",
|
||||
"value": -10,
|
||||
}
|
||||
|
||||
# Test set_duration with negative state
|
||||
hass.states.async_set("sensor.valve_duration", -10)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 0
|
||||
|
||||
|
||||
async def test_valve_with_duration_characteristics(
|
||||
hass: HomeAssistant, hk_driver, events: list[Event]
|
||||
) -> None:
|
||||
"""Test valve with set duration and remaining duration characteristics."""
|
||||
entity_id = "switch.sprinkler"
|
||||
|
||||
# Test with duration and end time entities linked
|
||||
hass.states.async_set(entity_id, STATE_OFF)
|
||||
hass.states.async_set("input_number.valve_duration", "900")
|
||||
hass.states.async_set("sensor.valve_end_time", dt_util.utcnow().isoformat())
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Using Valve instead of ValveSwitch
|
||||
acc = Valve(
|
||||
hass,
|
||||
hk_driver,
|
||||
"Valve",
|
||||
entity_id,
|
||||
5,
|
||||
{
|
||||
"linked_valve_duration": "input_number.valve_duration",
|
||||
"linked_valve_end_time": "sensor.valve_end_time",
|
||||
},
|
||||
)
|
||||
acc.run()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with freeze_time(dt_util.utcnow()):
|
||||
hass.states.async_set(
|
||||
"sensor.valve_end_time",
|
||||
(dt_util.utcnow() + timedelta(seconds=600)).isoformat(),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert acc.get_duration() == 900
|
||||
assert acc.get_remaining_duration() == 600
|
||||
|
@ -15,6 +15,8 @@ from homeassistant.components.homekit.const import (
|
||||
CONF_LINKED_BATTERY_SENSOR,
|
||||
CONF_LINKED_DOORBELL_SENSOR,
|
||||
CONF_LINKED_MOTION_SENSOR,
|
||||
CONF_LINKED_VALVE_DURATION,
|
||||
CONF_LINKED_VALVE_END_TIME,
|
||||
CONF_LOW_BATTERY_THRESHOLD,
|
||||
CONF_MAX_FPS,
|
||||
CONF_MAX_HEIGHT,
|
||||
@ -128,7 +130,25 @@ def test_validate_entity_config() -> None:
|
||||
}
|
||||
},
|
||||
{"switch.test": {CONF_TYPE: "invalid_type"}},
|
||||
{
|
||||
"switch.test": {
|
||||
CONF_TYPE: "sprinkler",
|
||||
CONF_LINKED_VALVE_DURATION: "number.valve_duration", # Must be input_number entity
|
||||
CONF_LINKED_VALVE_END_TIME: "datetime.valve_end_time", # Must be sensor (timestamp) entity
|
||||
}
|
||||
},
|
||||
{"fan.test": {CONF_TYPE: "invalid_type"}},
|
||||
{
|
||||
"valve.test": {
|
||||
CONF_LINKED_VALVE_END_TIME: "datetime.valve_end_time", # Must be sensor (timestamp) entity
|
||||
CONF_LINKED_VALVE_DURATION: "number.valve_duration", # Must be input_number
|
||||
}
|
||||
},
|
||||
{
|
||||
"valve.test": {
|
||||
CONF_TYPE: "sprinkler", # Extra keys not allowed
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
for conf in configs:
|
||||
@ -212,6 +232,19 @@ def test_validate_entity_config() -> None:
|
||||
assert vec({"switch.demo": {CONF_TYPE: TYPE_VALVE}}) == {
|
||||
"switch.demo": {CONF_TYPE: TYPE_VALVE, CONF_LOW_BATTERY_THRESHOLD: 20}
|
||||
}
|
||||
config = {
|
||||
CONF_TYPE: TYPE_SPRINKLER,
|
||||
CONF_LINKED_VALVE_DURATION: "input_number.valve_duration",
|
||||
CONF_LINKED_VALVE_END_TIME: "sensor.valve_end_time",
|
||||
}
|
||||
assert vec({"switch.sprinkler": config}) == {
|
||||
"switch.sprinkler": {
|
||||
CONF_TYPE: TYPE_SPRINKLER,
|
||||
CONF_LINKED_VALVE_DURATION: "input_number.valve_duration",
|
||||
CONF_LINKED_VALVE_END_TIME: "sensor.valve_end_time",
|
||||
CONF_LOW_BATTERY_THRESHOLD: DEFAULT_LOW_BATTERY_THRESHOLD,
|
||||
}
|
||||
}
|
||||
assert vec({"sensor.co": {CONF_THRESHOLD_CO: 500}}) == {
|
||||
"sensor.co": {CONF_THRESHOLD_CO: 500, CONF_LOW_BATTERY_THRESHOLD: 20}
|
||||
}
|
||||
@ -244,6 +277,17 @@ def test_validate_entity_config() -> None:
|
||||
CONF_LOW_BATTERY_THRESHOLD: DEFAULT_LOW_BATTERY_THRESHOLD,
|
||||
}
|
||||
}
|
||||
config = {
|
||||
CONF_LINKED_VALVE_DURATION: "input_number.valve_duration",
|
||||
CONF_LINKED_VALVE_END_TIME: "sensor.valve_end_time",
|
||||
}
|
||||
assert vec({"valve.demo": config}) == {
|
||||
"valve.demo": {
|
||||
CONF_LINKED_VALVE_DURATION: "input_number.valve_duration",
|
||||
CONF_LINKED_VALVE_END_TIME: "sensor.valve_end_time",
|
||||
CONF_LOW_BATTERY_THRESHOLD: DEFAULT_LOW_BATTERY_THRESHOLD,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_validate_media_player_features() -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user