"""Test ZHA analog output.""" from collections.abc import Callable, Coroutine from unittest.mock import call, patch import pytest from zhaquirks.builder import NumberDeviceClass, QuirkBuilder from zigpy.device import Device from zigpy.profiles import zha from zigpy.typing import UNDEFINED from zigpy.zcl.clusters import general import zigpy.zcl.foundation as zcl_f from homeassistant.components.homeassistant import ( DOMAIN as HOMEASSISTANT_DOMAIN, SERVICE_UPDATE_ENTITY, ) from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN from homeassistant.components.zha.helpers import ( ZHADeviceProxy, ZHAGatewayProxy, get_zha_gateway, get_zha_gateway_proxy, ) from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from .common import find_entity_id, send_attributes_report, update_attribute_cache from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE @pytest.fixture(autouse=True) def number_platform_only(): """Only set up the number and required base platforms to speed up tests.""" with patch( "homeassistant.components.zha.PLATFORMS", ( Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.LIGHT, Platform.NUMBER, Platform.SELECT, Platform.SENSOR, ), ): yield async def test_number( hass: HomeAssistant, setup_zha: Callable[..., Coroutine[None]], zigpy_device_mock: Callable[..., Device], ) -> None: """Test ZHA number platform.""" await setup_zha() gateway = get_zha_gateway(hass) gateway_proxy: ZHAGatewayProxy = get_zha_gateway_proxy(hass) zigpy_device = zigpy_device_mock( { 1: { SIG_EP_TYPE: zha.DeviceType.LEVEL_CONTROL_SWITCH, SIG_EP_INPUT: [ general.AnalogOutput.cluster_id, general.Basic.cluster_id, ], SIG_EP_OUTPUT: [], SIG_EP_PROFILE: zha.PROFILE_ID, } } ) cluster = zigpy_device.endpoints[1].analog_output cluster.PLUGGED_ATTR_READS = { "max_present_value": 100.0, "min_present_value": 1.0, "relinquish_default": 50.0, "resolution": 1.1, "description": "PWM1", "engineering_units": 98, "application_type": 4 * 0x10000, } update_attribute_cache(cluster) cluster.PLUGGED_ATTR_READS["present_value"] = 15.0 gateway.get_or_create_device(zigpy_device) await gateway.async_device_initialized(zigpy_device) await hass.async_block_till_done(wait_background_tasks=True) zha_device_proxy: ZHADeviceProxy = gateway_proxy.get_device_proxy(zigpy_device.ieee) entity_id = find_entity_id(Platform.NUMBER, zha_device_proxy, hass) assert entity_id is not None hass_state = hass.states.get(entity_id) assert hass_state is not None assert hass_state.state == "15.0" # test attributes assert hass_state.attributes.get("min") == 1.0 assert hass_state.attributes.get("max") == 100.0 assert hass_state.attributes.get("step") == 1.1 assert hass_state.attributes.get("icon") == "mdi:percent" assert hass_state.attributes.get("unit_of_measurement") == "%" assert hass_state.attributes.get("mode") == "auto" assert hass_state.attributes.get("device_class") is None assert ( hass.states.get(entity_id).attributes.get("friendly_name") == "FakeManufacturer FakeModel PWM1" ) # change value from device await send_attributes_report(hass, cluster, {0x0055: 15}) assert hass.states.get(entity_id).state == "15.0" # update value from device await send_attributes_report(hass, cluster, {0x0055: 20}) assert hass.states.get(entity_id).state == "20.0" # change value from HA with patch( "zigpy.zcl.Cluster.write_attributes", return_value=[zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS], ): # set value via UI await hass.services.async_call( NUMBER_DOMAIN, "set_value", {"entity_id": entity_id, "value": 30.0}, blocking=True, ) assert cluster.write_attributes.mock_calls == [ call({"present_value": 30.0}, manufacturer=UNDEFINED) ] cluster.PLUGGED_ATTR_READS["present_value"] = 30.0 # update device value with failed attribute report cluster.PLUGGED_ATTR_READS["present_value"] = 40.0 await async_setup_component(hass, HOMEASSISTANT_DOMAIN, {}) await hass.async_block_till_done() await hass.services.async_call( HOMEASSISTANT_DOMAIN, SERVICE_UPDATE_ENTITY, {"entity_id": entity_id}, blocking=True, ) assert hass.states.get(entity_id).state == "40.0" assert "present_value" in cluster.read_attributes.call_args[0][0] async def test_number_quirks_v2_metadata( hass: HomeAssistant, setup_zha: Callable[..., Coroutine[None]], zigpy_device_mock: Callable[..., Device], ) -> None: """Test that mode and device_class from quirks v2 metadata are passed through.""" ( QuirkBuilder("Test Manf", "Test Number Model") .number( attribute_name="current_level", cluster_id=general.LevelControl.cluster_id, min_value=0, max_value=254, mode="box", device_class=NumberDeviceClass.TEMPERATURE, fallback_name="Level", ) .add_to_registry() ) await setup_zha() gateway = get_zha_gateway(hass) zigpy_device = zigpy_device_mock( { 1: { SIG_EP_INPUT: [general.LevelControl.cluster_id], SIG_EP_OUTPUT: [], SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, SIG_EP_PROFILE: zha.PROFILE_ID, } }, manufacturer="Test Manf", model="Test Number Model", ) gateway.get_or_create_device(zigpy_device) await gateway.async_device_initialized(zigpy_device) await hass.async_block_till_done(wait_background_tasks=True) entity_id = "number.test_manf_test_number_model_temperature" hass_state = hass.states.get(entity_id) assert hass_state is not None assert hass_state.attributes.get("mode") == "box" assert hass_state.attributes.get("device_class") == "temperature"