"""The tests for the Template number platform.""" from homeassistant import setup 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.number import ( ATTR_MAX, ATTR_MIN, ATTR_STEP, ATTR_VALUE as NUMBER_ATTR_VALUE, DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE as NUMBER_SERVICE_SET_VALUE, ) from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import Context from homeassistant.helpers.entity_registry import async_get from tests.common import assert_setup_component, async_capture_events _TEST_NUMBER = "number.template_number" # Represent for number's value _VALUE_INPUT_NUMBER = "input_number.value" # Represent for number's minimum _MINIMUM_INPUT_NUMBER = "input_number.minimum" # Represent for number's maximum _MAXIMUM_INPUT_NUMBER = "input_number.maximum" # Represent for number's step _STEP_INPUT_NUMBER = "input_number.step" # Config for `_VALUE_INPUT_NUMBER` _VALUE_INPUT_NUMBER_CONFIG = { "value": { "min": 0.0, "max": 100.0, "name": "Value", "step": 1.0, "mode": "slider", } } async def test_missing_optional_config(hass): """Test: missing optional template is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "number": { "state": "{{ 4 }}", "set_value": {"service": "script.set_value"}, "step": "{{ 1 }}", } } }, ) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() _verify(hass, 4, 1, 0.0, 100.0) async def test_missing_required_keys(hass): """Test: missing required fields will fail.""" with assert_setup_component(0, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "number": { "set_value": {"service": "script.set_value"}, } } }, ) with assert_setup_component(0, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "number": { "state": "{{ 4 }}", } } }, ) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() assert hass.states.async_all("number") == [] async def test_all_optional_config(hass): """Test: including all optional templates is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "number": { "state": "{{ 4 }}", "set_value": {"service": "script.set_value"}, "min": "{{ 3 }}", "max": "{{ 5 }}", "step": "{{ 1 }}", } } }, ) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() _verify(hass, 4, 1, 3, 5) async def test_templates_with_entities(hass, calls): """Test templates with values from other entities.""" with assert_setup_component(4, "input_number"): assert await setup.async_setup_component( hass, "input_number", { "input_number": { **_VALUE_INPUT_NUMBER_CONFIG, "step": { "min": 0.0, "max": 100.0, "name": "Step", "step": 1.0, "mode": "slider", }, "minimum": { "min": 0.0, "max": 100.0, "name": "Minimum", "step": 1.0, "mode": "slider", }, "maximum": { "min": 0.0, "max": 100.0, "name": "Maximum", "step": 1.0, "mode": "slider", }, } }, ) with assert_setup_component(1, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "unique_id": "b", "number": { "state": f"{{{{ states('{_VALUE_INPUT_NUMBER}') }}}}", "step": f"{{{{ states('{_STEP_INPUT_NUMBER}') }}}}", "min": f"{{{{ states('{_MINIMUM_INPUT_NUMBER}') }}}}", "max": f"{{{{ states('{_MAXIMUM_INPUT_NUMBER}') }}}}", "set_value": [ { "service": "input_number.set_value", "data_template": { "entity_id": _VALUE_INPUT_NUMBER, "value": "{{ value }}", }, }, { "service": "test.automation", "data_template": { "action": "set_value", "caller": "{{ this.entity_id }}", "value": "{{ value }}", }, }, ], "optimistic": True, "unique_id": "a", }, } }, ) hass.states.async_set(_VALUE_INPUT_NUMBER, 4) hass.states.async_set(_STEP_INPUT_NUMBER, 1) hass.states.async_set(_MINIMUM_INPUT_NUMBER, 3) hass.states.async_set(_MAXIMUM_INPUT_NUMBER, 5) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() ent_reg = async_get(hass) entry = ent_reg.async_get(_TEST_NUMBER) assert entry assert entry.unique_id == "b-a" _verify(hass, 4, 1, 3, 5) await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _VALUE_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 5}, blocking=True, ) await hass.async_block_till_done() _verify(hass, 5, 1, 3, 5) await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _STEP_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 2}, blocking=True, ) await hass.async_block_till_done() _verify(hass, 5, 2, 3, 5) await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _MINIMUM_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 2}, blocking=True, ) await hass.async_block_till_done() _verify(hass, 5, 2, 2, 5) await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _MAXIMUM_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 6}, blocking=True, ) await hass.async_block_till_done() _verify(hass, 5, 2, 2, 6) await hass.services.async_call( NUMBER_DOMAIN, NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _TEST_NUMBER, NUMBER_ATTR_VALUE: 2}, blocking=True, ) _verify(hass, 2, 2, 2, 6) # Check this variable can be used in set_value script assert len(calls) == 1 assert calls[-1].data["action"] == "set_value" assert calls[-1].data["caller"] == _TEST_NUMBER assert calls[-1].data["value"] == 2 async def test_trigger_number(hass): """Test trigger based template number.""" events = async_capture_events(hass, "test_number_event") assert await setup.async_setup_component( hass, "template", { "template": [ {"invalid": "config"}, # Config after invalid should still be set up { "unique_id": "listening-test-event", "trigger": {"platform": "event", "event_type": "test_event"}, "number": [ { "name": "Hello Name", "unique_id": "hello_name-id", "state": "{{ trigger.event.data.beers_drank }}", "min": "{{ trigger.event.data.min_beers }}", "max": "{{ trigger.event.data.max_beers }}", "step": "{{ trigger.event.data.step }}", "set_value": {"event": "test_number_event"}, "optimistic": True, }, ], }, ], }, ) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() state = hass.states.get("number.hello_name") assert state is not None assert state.state == STATE_UNKNOWN assert state.attributes["min"] == 0.0 assert state.attributes["max"] == 100.0 assert state.attributes["step"] == 1.0 context = Context() hass.bus.async_fire( "test_event", {"beers_drank": 3, "min_beers": 1.0, "max_beers": 5.0, "step": 0.5}, context=context, ) await hass.async_block_till_done() state = hass.states.get("number.hello_name") assert state is not None assert state.state == "3.0" assert state.attributes["min"] == 1.0 assert state.attributes["max"] == 5.0 assert state.attributes["step"] == 0.5 await hass.services.async_call( NUMBER_DOMAIN, NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: "number.hello_name", NUMBER_ATTR_VALUE: 2}, blocking=True, ) assert len(events) == 1 assert events[0].event_type == "test_number_event" def _verify( hass, expected_value, expected_step, expected_minimum, expected_maximum, ): """Verify number's state.""" state = hass.states.get(_TEST_NUMBER) attributes = state.attributes assert state.state == str(float(expected_value)) assert attributes.get(ATTR_STEP) == float(expected_step) assert attributes.get(ATTR_MAX) == float(expected_maximum) assert attributes.get(ATTR_MIN) == float(expected_minimum) async def test_icon_template(hass): """Test template numbers with icon templates.""" with assert_setup_component(1, "input_number"): assert await setup.async_setup_component( hass, "input_number", {"input_number": _VALUE_INPUT_NUMBER_CONFIG}, ) with assert_setup_component(1, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "unique_id": "b", "number": { "state": f"{{{{ states('{_VALUE_INPUT_NUMBER}') }}}}", "step": 1, "min": 0, "max": 100, "set_value": { "service": "input_number.set_value", "data_template": { "entity_id": _VALUE_INPUT_NUMBER, "value": "{{ value }}", }, }, "icon": "{% if ((states.input_number.value.state or 0) | int) > 50 %}mdi:greater{% else %}mdi:less{% endif %}", }, } }, ) hass.states.async_set(_VALUE_INPUT_NUMBER, 49) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() state = hass.states.get(_TEST_NUMBER) assert float(state.state) == 49 assert state.attributes[ATTR_ICON] == "mdi:less" await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _VALUE_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 51}, blocking=True, ) await hass.async_block_till_done() state = hass.states.get(_TEST_NUMBER) assert float(state.state) == 51 assert state.attributes[ATTR_ICON] == "mdi:greater" async def test_icon_template_with_trigger(hass): """Test template numbers with icon templates.""" with assert_setup_component(1, "input_number"): assert await setup.async_setup_component( hass, "input_number", {"input_number": _VALUE_INPUT_NUMBER_CONFIG}, ) with assert_setup_component(1, "template"): assert await setup.async_setup_component( hass, "template", { "template": { "trigger": {"platform": "state", "entity_id": _VALUE_INPUT_NUMBER}, "unique_id": "b", "number": { "state": "{{ trigger.to_state.state }}", "step": 1, "min": 0, "max": 100, "set_value": { "service": "input_number.set_value", "data_template": { "entity_id": _VALUE_INPUT_NUMBER, "value": "{{ value }}", }, }, "icon": "{% if ((trigger.to_state.state or 0) | int) > 50 %}mdi:greater{% else %}mdi:less{% endif %}", }, } }, ) hass.states.async_set(_VALUE_INPUT_NUMBER, 49) await hass.async_block_till_done() await hass.async_start() await hass.async_block_till_done() state = hass.states.get(_TEST_NUMBER) assert float(state.state) == 49 assert state.attributes[ATTR_ICON] == "mdi:less" await hass.services.async_call( INPUT_NUMBER_DOMAIN, INPUT_NUMBER_SERVICE_SET_VALUE, {CONF_ENTITY_ID: _VALUE_INPUT_NUMBER, INPUT_NUMBER_ATTR_VALUE: 51}, blocking=True, ) await hass.async_block_till_done() state = hass.states.get(_TEST_NUMBER) assert float(state.state) == 51 assert state.attributes[ATTR_ICON] == "mdi:greater"