"""Test numbers for ToGrill integration.""" from datetime import timedelta from unittest.mock import Mock from bleak.exc import BleakError import pytest from syrupy.assertion import SnapshotAssertion from togrill_bluetooth.exceptions import BaseError from togrill_bluetooth.packets import ( PacketA0Notify, PacketA6Write, PacketA7Write, PacketA8Notify, PacketA300Write, PacketA301Write, ) from homeassistant.components.number import ( ATTR_VALUE, DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE, ) from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import TOGRILL_SERVICE_INFO, setup_entry from tests.common import MockConfigEntry, snapshot_platform from tests.components.bluetooth import inject_bluetooth_service_info @pytest.mark.parametrize( "packets", [ pytest.param([], id="no_data"), pytest.param( [ PacketA0Notify( battery=45, version_major=1, version_minor=5, function_type=1, probe_count=2, ambient=False, alarm_interval=5, alarm_sound=True, ), PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), PacketA8Notify(probe=2, alarm_type=None), ], id="one_probe_with_target_alarm", ), ], ) async def test_setup( hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, mock_entry: MockConfigEntry, mock_client: Mock, packets, ) -> None: """Test the numbers.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry, [Platform.NUMBER]) for packet in packets: mock_client.mocked_notify(packet) await snapshot_platform(hass, entity_registry, snapshot, mock_entry.entry_id) @pytest.mark.parametrize( "packets", [ pytest.param([], id="no_data"), pytest.param( [ PacketA8Notify( probe=0, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=5.0, temperature_2=300.0, ), ], id="ambient_with_range", ), pytest.param( [ PacketA8Notify( probe=0, alarm_type=None, temperature_1=5.0, temperature_2=300.0, ), ], id="ambient_wrong_alarm_type", ), ], ) async def test_setup_with_ambient( hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, mock_entry_with_ambient: MockConfigEntry, mock_client: Mock, packets, ) -> None: """Test the numbers with ambient sensor enabled.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry_with_ambient, [Platform.NUMBER]) for packet in packets: mock_client.mocked_notify(packet) await snapshot_platform( hass, entity_registry, snapshot, mock_entry_with_ambient.entry_id ) @pytest.mark.parametrize( ("packets", "entity_id", "value", "write_packet"), [ pytest.param( [ PacketA8Notify( probe=0, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=5.0, temperature_2=300.0, ), ], "number.pro_05_ambient_minimum_temperature", 10.0, PacketA300Write(probe=0, minimum=10.0, maximum=300.0), id="ambient_minimum", ), pytest.param( [ PacketA8Notify( probe=0, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=5.0, temperature_2=300.0, ), ], "number.pro_05_ambient_minimum_temperature", 0.0, PacketA300Write(probe=0, minimum=None, maximum=300.0), id="ambient_minimum_clear", ), pytest.param( [ PacketA8Notify( probe=0, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=5.0, temperature_2=300.0, ), ], "number.pro_05_ambient_maximum_temperature", 350.0, PacketA300Write(probe=0, minimum=5.0, maximum=350.0), id="ambient_maximum", ), pytest.param( [ PacketA8Notify( probe=0, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=5.0, temperature_2=300.0, ), ], "number.pro_05_ambient_maximum_temperature", 0.0, PacketA300Write(probe=0, minimum=5.0, maximum=None), id="ambient_maximum_clear", ), ], ) async def test_set_ambient_number( hass: HomeAssistant, mock_entry_with_ambient: MockConfigEntry, mock_client: Mock, packets, entity_id, value, write_packet, ) -> None: """Test setting ambient temperature numbers.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry_with_ambient, [Platform.NUMBER]) for packet in packets: mock_client.mocked_notify(packet) await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, service_data={ ATTR_VALUE: value, }, target={ ATTR_ENTITY_ID: entity_id, }, blocking=True, ) mock_client.write.assert_any_call(write_packet) @pytest.mark.parametrize( ("packets", "entity_id", "value", "write_packet"), [ pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ], "number.probe_1_target_temperature", 100.0, PacketA301Write(probe=1, target=100), id="probe", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ], "number.probe_1_target_temperature", 0.0, PacketA301Write(probe=1, target=None), id="probe_clear", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=50.0, temperature_2=80.0, ), ], "number.probe_1_minimum_temperature", 100.0, PacketA300Write(probe=1, minimum=100.0, maximum=80.0), id="minimum", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=None, temperature_2=80.0, ), ], "number.probe_1_minimum_temperature", 0.0, PacketA300Write(probe=1, minimum=None, maximum=80.0), id="minimum_clear", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=50.0, temperature_2=80.0, ), ], "number.probe_1_maximum_temperature", 100.0, PacketA300Write(probe=1, minimum=50.0, maximum=100.0), id="maximum", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_RANGE, temperature_1=50.0, temperature_2=None, ), ], "number.probe_1_maximum_temperature", 0.0, PacketA300Write(probe=1, minimum=50.0, maximum=None), id="maximum_clear", ), pytest.param( [ PacketA0Notify( battery=45, version_major=1, version_minor=5, function_type=1, probe_count=2, ambient=False, alarm_interval=5, alarm_sound=True, ) ], "number.pro_05_alarm_interval", 15, PacketA6Write(temperature_unit=None, alarm_interval=15), id="alarm_interval", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ], "number.probe_1_timer", 10.0, PacketA7Write(probe=1, time=timedelta(minutes=10), unknown=1), id="timer", ), pytest.param( [ PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ], "number.probe_1_timer", 0.0, PacketA7Write(probe=1, time=timedelta(0), unknown=0), id="timer_stop", ), ], ) async def test_set_number( hass: HomeAssistant, mock_entry: MockConfigEntry, mock_client: Mock, packets, entity_id, value, write_packet, ) -> None: """Test the number set.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry, [Platform.NUMBER]) for packet in packets: mock_client.mocked_notify(packet) await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, service_data={ ATTR_VALUE: value, }, target={ ATTR_ENTITY_ID: entity_id, }, blocking=True, ) mock_client.write.assert_any_call(write_packet) @pytest.mark.parametrize( ("error", "message"), [ pytest.param( BleakError("Some error"), "Communication failed with the device", id="bleak", ), pytest.param( BaseError("Some error"), "Data was rejected by device", id="base", ), ], ) async def test_set_number_write_error( hass: HomeAssistant, mock_entry: MockConfigEntry, mock_client: Mock, error, message, ) -> None: """Test the number set.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry, [Platform.NUMBER]) mock_client.mocked_notify( PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ) mock_client.write.side_effect = error with pytest.raises(HomeAssistantError, match=message): await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, service_data={ ATTR_VALUE: 100, }, target={ ATTR_ENTITY_ID: "number.probe_1_target_temperature", }, blocking=True, ) async def test_set_number_disconnected( hass: HomeAssistant, mock_entry: MockConfigEntry, mock_client: Mock, ) -> None: """Test the number set.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry, [Platform.NUMBER]) mock_client.mocked_notify( PacketA8Notify( probe=1, alarm_type=PacketA8Notify.AlarmType.TEMPERATURE_TARGET, temperature_1=50.0, ), ) mock_client.is_connected = False with pytest.raises(HomeAssistantError): await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, service_data={ ATTR_VALUE: 100, }, target={ ATTR_ENTITY_ID: "number.probe_1_target_temperature", }, blocking=True, ) async def test_timer_readback( hass: HomeAssistant, mock_entry: MockConfigEntry, mock_client: Mock, ) -> None: """Test that a running timer is reported back in minutes.""" inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) await setup_entry(hass, mock_entry, [Platform.NUMBER]) mock_client.mocked_notify( PacketA8Notify( probe=1, alarm_type=None, time=timedelta(minutes=30), ) ) await hass.async_block_till_done() state = hass.states.get("number.probe_1_timer") assert state is not None assert float(state.state) == 30.0