From 2d1a672de5c464a313eab82da331a14acb610635 Mon Sep 17 00:00:00 2001 From: Panda-NZ <32557789+pandanz@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:44:23 +1300 Subject: [PATCH] Add ambient temperature sensor to ToGrill (#159798) --- .../components/togrill/config_flow.py | 3 +- homeassistant/components/togrill/const.py | 1 + homeassistant/components/togrill/sensor.py | 34 +- homeassistant/components/togrill/strings.json | 5 + tests/components/togrill/conftest.py | 21 +- .../togrill/snapshots/test_sensor.ambr | 1120 +++++++++++++++++ tests/components/togrill/test_config_flow.py | 2 + tests/components/togrill/test_sensor.py | 66 + 8 files changed, 1249 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/togrill/config_flow.py b/homeassistant/components/togrill/config_flow.py index 0415eadf260..46b2853779e 100644 --- a/homeassistant/components/togrill/config_flow.py +++ b/homeassistant/components/togrill/config_flow.py @@ -19,7 +19,7 @@ from homeassistant.const import CONF_ADDRESS, CONF_MODEL from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import AbortFlow -from .const import CONF_PROBE_COUNT, DOMAIN +from .const import CONF_HAS_AMBIENT, CONF_PROBE_COUNT, DOMAIN from .coordinator import LOGGER _TIMEOUT = 10 @@ -48,6 +48,7 @@ async def read_config_data( CONF_MODEL: info.name, CONF_ADDRESS: info.address, CONF_PROBE_COUNT: packet_a0.probe_count, + CONF_HAS_AMBIENT: packet_a0.ambient, } diff --git a/homeassistant/components/togrill/const.py b/homeassistant/components/togrill/const.py index dd2fe820919..9bf3276a1e4 100644 --- a/homeassistant/components/togrill/const.py +++ b/homeassistant/components/togrill/const.py @@ -5,4 +5,5 @@ DOMAIN = "togrill" MAX_PROBE_COUNT = 6 CONF_PROBE_COUNT = "probe_count" +CONF_HAS_AMBIENT = "has_ambient" CONF_VERSION = "version" diff --git a/homeassistant/components/togrill/sensor.py b/homeassistant/components/togrill/sensor.py index 0b85a09145c..affe03f6d6b 100644 --- a/homeassistant/components/togrill/sensor.py +++ b/homeassistant/components/togrill/sensor.py @@ -20,7 +20,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import ToGrillConfigEntry -from .const import CONF_PROBE_COUNT, MAX_PROBE_COUNT +from .const import CONF_HAS_AMBIENT, CONF_PROBE_COUNT, MAX_PROBE_COUNT from .coordinator import ToGrillCoordinator from .entity import ToGrillEntity @@ -63,6 +63,27 @@ def _get_temperature_description(probe_number: int): ) +def _get_ambient_temperature(packet: Packet) -> StateType: + """Extract ambient temperature from packet. + + The ambient temperature is the last value in the temperatures list + when the device has an ambient sensor. + """ + assert isinstance(packet, PacketA1Notify) + if not packet.temperatures: + return None + # Ambient is always the last temperature value + temperature = packet.temperatures[-1] + if temperature is None: + return None + return temperature + + +def _ambient_supported(config: Mapping[str, Any]) -> bool: + """Check if ambient sensor is supported.""" + return config.get(CONF_HAS_AMBIENT, False) + + ENTITY_DESCRIPTIONS = ( ToGrillSensorEntityDescription( key="battery", @@ -78,6 +99,17 @@ ENTITY_DESCRIPTIONS = ( _get_temperature_description(probe_number) for probe_number in range(1, MAX_PROBE_COUNT + 1) ], + ToGrillSensorEntityDescription( + key="ambient_temperature", + translation_key="ambient_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + packet_type=PacketA1Notify.type, + packet_extract=_get_ambient_temperature, + entity_supported=_ambient_supported, + ), ) diff --git a/homeassistant/components/togrill/strings.json b/homeassistant/components/togrill/strings.json index 029366dd378..2db82fe6f46 100644 --- a/homeassistant/components/togrill/strings.json +++ b/homeassistant/components/togrill/strings.json @@ -98,6 +98,11 @@ "well_done": "Well done" } } + }, + "sensor": { + "ambient_temperature": { + "name": "Ambient temperature" + } } }, "exceptions": { diff --git a/tests/components/togrill/conftest.py b/tests/components/togrill/conftest.py index c58bc0698a9..a4811632642 100644 --- a/tests/components/togrill/conftest.py +++ b/tests/components/togrill/conftest.py @@ -7,7 +7,11 @@ import pytest from togrill_bluetooth.client import Client from togrill_bluetooth.packets import Packet, PacketA0Notify, PacketNotify -from homeassistant.components.togrill.const import CONF_PROBE_COUNT, DOMAIN +from homeassistant.components.togrill.const import ( + CONF_HAS_AMBIENT, + CONF_PROBE_COUNT, + DOMAIN, +) from homeassistant.const import CONF_ADDRESS, CONF_MODEL from . import TOGRILL_SERVICE_INFO @@ -29,6 +33,21 @@ def mock_entry() -> MockConfigEntry: ) +@pytest.fixture +def mock_entry_with_ambient() -> MockConfigEntry: + """Create hass config fixture with ambient sensor enabled.""" + return MockConfigEntry( + domain=DOMAIN, + data={ + CONF_ADDRESS: TOGRILL_SERVICE_INFO.address, + CONF_MODEL: "Pro-05", + CONF_PROBE_COUNT: 2, + CONF_HAS_AMBIENT: True, + }, + unique_id=TOGRILL_SERVICE_INFO.address, + ) + + @pytest.fixture(scope="module") def mock_unload_entry() -> Generator[AsyncMock]: """Override async_unload_entry.""" diff --git a/tests/components/togrill/snapshots/test_sensor.ambr b/tests/components/togrill/snapshots/test_sensor.ambr index d92dbca0a04..3f1d1fd196d 100644 --- a/tests/components/togrill/snapshots/test_sensor.ambr +++ b/tests/components/togrill/snapshots/test_sensor.ambr @@ -671,3 +671,1123 @@ 'state': 'unavailable', }) # --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.pro_05_ambient_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ambient temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ambient_temperature', + 'unique_id': '00000000-0000-0000-0000-000000000001_ambient_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.pro_05_ambient_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Pro-05 Ambient temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.pro_05_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.pro_05_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.pro_05_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Pro-05 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.pro_05_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.probe_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.probe_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.probe_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_empty_temperatures][sensor.probe_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.pro_05_ambient_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ambient temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ambient_temperature', + 'unique_id': '00000000-0000-0000-0000-000000000001_ambient_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.pro_05_ambient_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Pro-05 Ambient temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.pro_05_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.pro_05_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.pro_05_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Pro-05 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.pro_05_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.probe_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.probe_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.probe_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_data][sensor.probe_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.pro_05_ambient_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ambient temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ambient_temperature', + 'unique_id': '00000000-0000-0000-0000-000000000001_ambient_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.pro_05_ambient_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Pro-05 Ambient temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.pro_05_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.pro_05_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.pro_05_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Pro-05 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.pro_05_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.probe_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.probe_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.probe_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_none][sensor.probe_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.pro_05_ambient_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ambient temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ambient_temperature', + 'unique_id': '00000000-0000-0000-0000-000000000001_ambient_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.pro_05_ambient_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Pro-05 Ambient temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.pro_05_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.pro_05_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.pro_05_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Pro-05 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.pro_05_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.probe_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.probe_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.probe_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[ambient_temp_with_missing_probe][sensor.probe_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.pro_05_ambient_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ambient temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ambient_temperature', + 'unique_id': '00000000-0000-0000-0000-000000000001_ambient_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.pro_05_ambient_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Pro-05 Ambient temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.pro_05_ambient_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.pro_05_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.pro_05_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_battery', + 'unit_of_measurement': '%', + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.pro_05_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Pro-05 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.pro_05_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.probe_1_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_1', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.probe_1_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 1 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_1_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.probe_2_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.probe_2_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'togrill', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00000000-0000-0000-0000-000000000001_temperature_2', + 'unit_of_measurement': , + }) +# --- +# name: test_setup_with_ambient[no_data][sensor.probe_2_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Probe 2 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.probe_2_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- diff --git a/tests/components/togrill/test_config_flow.py b/tests/components/togrill/test_config_flow.py index 618f82ccd9e..20e4edd67d1 100644 --- a/tests/components/togrill/test_config_flow.py +++ b/tests/components/togrill/test_config_flow.py @@ -42,6 +42,7 @@ async def test_user_selection( "address": TOGRILL_SERVICE_INFO.address, "model": "Pro-05", "probe_count": 0, + "has_ambient": False, } assert result["title"] == "Pro-05" assert result["result"].unique_id == TOGRILL_SERVICE_INFO.address @@ -176,6 +177,7 @@ async def test_bluetooth( "address": TOGRILL_SERVICE_INFO.address, "model": "Pro-05", "probe_count": 0, + "has_ambient": False, } assert result["title"] == "Pro-05" assert result["result"].unique_id == TOGRILL_SERVICE_INFO.address diff --git a/tests/components/togrill/test_sensor.py b/tests/components/togrill/test_sensor.py index 913a295d379..dede1d29587 100644 --- a/tests/components/togrill/test_sensor.py +++ b/tests/components/togrill/test_sensor.py @@ -70,6 +70,50 @@ async def test_setup( await snapshot_platform(hass, entity_registry, snapshot, mock_entry.entry_id) +@pytest.mark.parametrize( + "packets", + [ + pytest.param([], id="no_data"), + pytest.param( + [PacketA1Notify([10, 20, 25])], + id="ambient_temp_data", + ), + pytest.param( + [PacketA1Notify([10, None, 25])], + id="ambient_temp_with_missing_probe", + ), + pytest.param( + [PacketA1Notify([])], + id="ambient_empty_temperatures", + ), + pytest.param( + [PacketA1Notify([10, 20, None])], + id="ambient_temp_none", + ), + ], +) +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 sensors with ambient temperature sensor enabled.""" + + inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) + + await setup_entry(hass, mock_entry_with_ambient, [Platform.SENSOR]) + + for packet in packets: + mock_client.mocked_notify(packet) + + await snapshot_platform( + hass, entity_registry, snapshot, mock_entry_with_ambient.entry_id + ) + + async def test_device_disconnected( hass: HomeAssistant, mock_entry: MockConfigEntry, @@ -116,3 +160,25 @@ async def test_device_discovered( state = hass.states.get(entity_id) assert state assert state.state == "0" + + +async def test_ambient_sensor_not_created_without_config( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_entry: MockConfigEntry, + mock_client: Mock, +) -> None: + """Test ambient temperature sensor is not created when not configured.""" + inject_bluetooth_service_info(hass, TOGRILL_SERVICE_INFO) + + await setup_entry(hass, mock_entry, [Platform.SENSOR]) + + entity_id = "sensor.pro_05_ambient_temperature" + + # Entity should not exist when CONF_HAS_AMBIENT is not set + state = hass.states.get(entity_id) + assert state is None + + # Verify it's not in the entity registry + entry = entity_registry.async_get(entity_id) + assert entry is None