mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Add unit_of_measurement to template numbers (#122862)
This commit is contained in:
parent
8e0b2b752c
commit
56dfb2c734
@ -116,6 +116,11 @@ def generate_schema(domain: str, flow_type: str) -> vol.Schema:
|
|||||||
vol.Required(CONF_STEP, default=DEFAULT_STEP): selector.NumberSelector(
|
vol.Required(CONF_STEP, default=DEFAULT_STEP): selector.NumberSelector(
|
||||||
selector.NumberSelectorConfig(mode=selector.NumberSelectorMode.BOX),
|
selector.NumberSelectorConfig(mode=selector.NumberSelectorMode.BOX),
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(
|
||||||
|
selector.TextSelectorConfig(
|
||||||
|
type=selector.TextSelectorType.TEXT, multiline=False
|
||||||
|
)
|
||||||
|
),
|
||||||
vol.Optional(CONF_SET_VALUE): selector.ActionSelector(),
|
vol.Optional(CONF_SET_VALUE): selector.ActionSelector(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ from homeassistant.const import (
|
|||||||
CONF_OPTIMISTIC,
|
CONF_OPTIMISTIC,
|
||||||
CONF_STATE,
|
CONF_STATE,
|
||||||
CONF_UNIQUE_ID,
|
CONF_UNIQUE_ID,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import config_validation as cv, selector
|
from homeassistant.helpers import config_validation as cv, selector
|
||||||
@ -55,6 +56,7 @@ NUMBER_SCHEMA = (
|
|||||||
vol.Required(CONF_STEP): cv.template,
|
vol.Required(CONF_STEP): cv.template,
|
||||||
vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template,
|
vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template,
|
||||||
vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): cv.template,
|
vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): cv.template,
|
||||||
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
}
|
}
|
||||||
@ -70,6 +72,7 @@ NUMBER_CONFIG_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA,
|
vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_MIN): cv.template,
|
vol.Optional(CONF_MIN): cv.template,
|
||||||
vol.Optional(CONF_MAX): cv.template,
|
vol.Optional(CONF_MAX): cv.template,
|
||||||
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||||
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
|
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -159,6 +162,7 @@ class TemplateNumber(TemplateEntity, NumberEntity):
|
|||||||
self._min_value_template = config[CONF_MIN]
|
self._min_value_template = config[CONF_MIN]
|
||||||
self._max_value_template = config[CONF_MAX]
|
self._max_value_template = config[CONF_MAX]
|
||||||
self._attr_assumed_state = self._optimistic = config.get(CONF_OPTIMISTIC)
|
self._attr_assumed_state = self._optimistic = config.get(CONF_OPTIMISTIC)
|
||||||
|
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||||
self._attr_native_step = DEFAULT_STEP
|
self._attr_native_step = DEFAULT_STEP
|
||||||
self._attr_native_min_value = DEFAULT_MIN_VALUE
|
self._attr_native_min_value = DEFAULT_MIN_VALUE
|
||||||
self._attr_native_max_value = DEFAULT_MAX_VALUE
|
self._attr_native_max_value = DEFAULT_MAX_VALUE
|
||||||
@ -230,6 +234,7 @@ class TriggerNumberEntity(TriggerEntity, NumberEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
super().__init__(hass, coordinator, config)
|
super().__init__(hass, coordinator, config)
|
||||||
|
|
||||||
self._command_set_value = Script(
|
self._command_set_value = Script(
|
||||||
hass,
|
hass,
|
||||||
config[CONF_SET_VALUE],
|
config[CONF_SET_VALUE],
|
||||||
@ -237,6 +242,8 @@ class TriggerNumberEntity(TriggerEntity, NumberEntity):
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> float | None:
|
def native_value(self) -> float | None:
|
||||||
"""Return the currently selected option."""
|
"""Return the currently selected option."""
|
||||||
|
@ -45,7 +45,8 @@
|
|||||||
"step": "Step value",
|
"step": "Step value",
|
||||||
"set_value": "Actions on set value",
|
"set_value": "Actions on set value",
|
||||||
"max": "Maximum value",
|
"max": "Maximum value",
|
||||||
"min": "Minimum value"
|
"min": "Minimum value",
|
||||||
|
"unit_of_measurement": "[%key:component::template::config::step::sensor::data::unit_of_measurement%]"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"device_id": "[%key:component::template::config::step::sensor::data_description::device_id%]"
|
"device_id": "[%key:component::template::config::step::sensor::data_description::device_id%]"
|
||||||
|
@ -101,6 +101,7 @@ from tests.typing import WebSocketGenerator
|
|||||||
"min": "0",
|
"min": "0",
|
||||||
"max": "100",
|
"max": "100",
|
||||||
"step": "0.1",
|
"step": "0.1",
|
||||||
|
"unit_of_measurement": "cm",
|
||||||
"set_value": {
|
"set_value": {
|
||||||
"action": "input_number.set_value",
|
"action": "input_number.set_value",
|
||||||
"target": {"entity_id": "input_number.test"},
|
"target": {"entity_id": "input_number.test"},
|
||||||
@ -111,6 +112,7 @@ from tests.typing import WebSocketGenerator
|
|||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"step": 0.1,
|
"step": 0.1,
|
||||||
|
"unit_of_measurement": "cm",
|
||||||
"set_value": {
|
"set_value": {
|
||||||
"action": "input_number.set_value",
|
"action": "input_number.set_value",
|
||||||
"target": {"entity_id": "input_number.test"},
|
"target": {"entity_id": "input_number.test"},
|
||||||
@ -454,6 +456,7 @@ def get_suggested(schema, key):
|
|||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"step": 0.1,
|
"step": 0.1,
|
||||||
|
"unit_of_measurement": "cm",
|
||||||
"set_value": {
|
"set_value": {
|
||||||
"action": "input_number.set_value",
|
"action": "input_number.set_value",
|
||||||
"target": {"entity_id": "input_number.test"},
|
"target": {"entity_id": "input_number.test"},
|
||||||
@ -464,6 +467,7 @@ def get_suggested(schema, key):
|
|||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"step": 0.1,
|
"step": 0.1,
|
||||||
|
"unit_of_measurement": "cm",
|
||||||
"set_value": {
|
"set_value": {
|
||||||
"action": "input_number.set_value",
|
"action": "input_number.set_value",
|
||||||
"target": {"entity_id": "input_number.test"},
|
"target": {"entity_id": "input_number.test"},
|
||||||
|
@ -17,7 +17,12 @@ from homeassistant.components.number import (
|
|||||||
SERVICE_SET_VALUE as NUMBER_SERVICE_SET_VALUE,
|
SERVICE_SET_VALUE as NUMBER_SERVICE_SET_VALUE,
|
||||||
)
|
)
|
||||||
from homeassistant.components.template import DOMAIN
|
from homeassistant.components.template import DOMAIN
|
||||||
from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN
|
from homeassistant.const import (
|
||||||
|
ATTR_ICON,
|
||||||
|
CONF_ENTITY_ID,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
from homeassistant.core import Context, HomeAssistant, ServiceCall
|
from homeassistant.core import Context, HomeAssistant, ServiceCall
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
|
|
||||||
@ -100,7 +105,7 @@ async def test_missing_optional_config(hass: HomeAssistant) -> None:
|
|||||||
await hass.async_start()
|
await hass.async_start()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
_verify(hass, 4, 1, 0.0, 100.0)
|
_verify(hass, 4, 1, 0.0, 100.0, None)
|
||||||
|
|
||||||
|
|
||||||
async def test_missing_required_keys(hass: HomeAssistant) -> None:
|
async def test_missing_required_keys(hass: HomeAssistant) -> None:
|
||||||
@ -152,6 +157,7 @@ async def test_all_optional_config(hass: HomeAssistant) -> None:
|
|||||||
"min": "{{ 3 }}",
|
"min": "{{ 3 }}",
|
||||||
"max": "{{ 5 }}",
|
"max": "{{ 5 }}",
|
||||||
"step": "{{ 1 }}",
|
"step": "{{ 1 }}",
|
||||||
|
"unit_of_measurement": "beer",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -161,7 +167,7 @@ async def test_all_optional_config(hass: HomeAssistant) -> None:
|
|||||||
await hass.async_start()
|
await hass.async_start()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
_verify(hass, 4, 1, 3, 5)
|
_verify(hass, 4, 1, 3, 5, "beer")
|
||||||
|
|
||||||
|
|
||||||
async def test_templates_with_entities(
|
async def test_templates_with_entities(
|
||||||
@ -249,7 +255,7 @@ async def test_templates_with_entities(
|
|||||||
assert entry
|
assert entry
|
||||||
assert entry.unique_id == "b-a"
|
assert entry.unique_id == "b-a"
|
||||||
|
|
||||||
_verify(hass, 4, 1, 3, 5)
|
_verify(hass, 4, 1, 3, 5, None)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
INPUT_NUMBER_DOMAIN,
|
INPUT_NUMBER_DOMAIN,
|
||||||
@ -258,7 +264,7 @@ async def test_templates_with_entities(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
_verify(hass, 5, 1, 3, 5)
|
_verify(hass, 5, 1, 3, 5, None)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
INPUT_NUMBER_DOMAIN,
|
INPUT_NUMBER_DOMAIN,
|
||||||
@ -267,7 +273,7 @@ async def test_templates_with_entities(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
_verify(hass, 5, 2, 3, 5)
|
_verify(hass, 5, 2, 3, 5, None)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
INPUT_NUMBER_DOMAIN,
|
INPUT_NUMBER_DOMAIN,
|
||||||
@ -276,7 +282,7 @@ async def test_templates_with_entities(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
_verify(hass, 5, 2, 2, 5)
|
_verify(hass, 5, 2, 2, 5, None)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
INPUT_NUMBER_DOMAIN,
|
INPUT_NUMBER_DOMAIN,
|
||||||
@ -285,7 +291,7 @@ async def test_templates_with_entities(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
_verify(hass, 5, 2, 2, 6)
|
_verify(hass, 5, 2, 2, 6, None)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
NUMBER_DOMAIN,
|
NUMBER_DOMAIN,
|
||||||
@ -293,7 +299,7 @@ async def test_templates_with_entities(
|
|||||||
{CONF_ENTITY_ID: _TEST_NUMBER, NUMBER_ATTR_VALUE: 2},
|
{CONF_ENTITY_ID: _TEST_NUMBER, NUMBER_ATTR_VALUE: 2},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
_verify(hass, 2, 2, 2, 6)
|
_verify(hass, 2, 2, 2, 6, None)
|
||||||
|
|
||||||
# Check this variable can be used in set_value script
|
# Check this variable can be used in set_value script
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
@ -323,6 +329,7 @@ async def test_trigger_number(hass: HomeAssistant) -> None:
|
|||||||
"min": "{{ trigger.event.data.min_beers }}",
|
"min": "{{ trigger.event.data.min_beers }}",
|
||||||
"max": "{{ trigger.event.data.max_beers }}",
|
"max": "{{ trigger.event.data.max_beers }}",
|
||||||
"step": "{{ trigger.event.data.step }}",
|
"step": "{{ trigger.event.data.step }}",
|
||||||
|
"unit_of_measurement": "beer",
|
||||||
"set_value": {"event": "test_number_event"},
|
"set_value": {"event": "test_number_event"},
|
||||||
"optimistic": True,
|
"optimistic": True,
|
||||||
},
|
},
|
||||||
@ -342,11 +349,17 @@ async def test_trigger_number(hass: HomeAssistant) -> None:
|
|||||||
assert state.attributes["min"] == 0.0
|
assert state.attributes["min"] == 0.0
|
||||||
assert state.attributes["max"] == 100.0
|
assert state.attributes["max"] == 100.0
|
||||||
assert state.attributes["step"] == 1.0
|
assert state.attributes["step"] == 1.0
|
||||||
|
assert state.attributes["unit_of_measurement"] == "beer"
|
||||||
|
|
||||||
context = Context()
|
context = Context()
|
||||||
hass.bus.async_fire(
|
hass.bus.async_fire(
|
||||||
"test_event",
|
"test_event",
|
||||||
{"beers_drank": 3, "min_beers": 1.0, "max_beers": 5.0, "step": 0.5},
|
{
|
||||||
|
"beers_drank": 3,
|
||||||
|
"min_beers": 1.0,
|
||||||
|
"max_beers": 5.0,
|
||||||
|
"step": 0.5,
|
||||||
|
},
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -374,6 +387,7 @@ def _verify(
|
|||||||
expected_step: int,
|
expected_step: int,
|
||||||
expected_minimum: int,
|
expected_minimum: int,
|
||||||
expected_maximum: int,
|
expected_maximum: int,
|
||||||
|
expected_unit_of_measurement: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Verify number's state."""
|
"""Verify number's state."""
|
||||||
state = hass.states.get(_TEST_NUMBER)
|
state = hass.states.get(_TEST_NUMBER)
|
||||||
@ -382,6 +396,7 @@ def _verify(
|
|||||||
assert attributes.get(ATTR_STEP) == float(expected_step)
|
assert attributes.get(ATTR_STEP) == float(expected_step)
|
||||||
assert attributes.get(ATTR_MAX) == float(expected_maximum)
|
assert attributes.get(ATTR_MAX) == float(expected_maximum)
|
||||||
assert attributes.get(ATTR_MIN) == float(expected_minimum)
|
assert attributes.get(ATTR_MIN) == float(expected_minimum)
|
||||||
|
assert attributes.get(CONF_UNIT_OF_MEASUREMENT) == expected_unit_of_measurement
|
||||||
|
|
||||||
|
|
||||||
async def test_icon_template(hass: HomeAssistant) -> None:
|
async def test_icon_template(hass: HomeAssistant) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user