From 12d577126acfe7f84546a466987a24563d45d01c Mon Sep 17 00:00:00 2001 From: Heikki Henriksen Date: Fri, 8 May 2026 11:59:52 +0200 Subject: [PATCH] prusalink: expose printer location as suggested_area Per @joostlek's review feedback on #169312: the printer's `location` field from /api/v1/info is a physical placement hint (e.g. "Workshop", "Garage"), which maps semantically to HA's `suggested_area` on `DeviceInfo` rather than to a sensor entity. - Drop the `location` sensor entity (and its strings/icons entries) - Set `suggested_area=info_data.get("location")` on `DeviceInfo` in the base entity, so the printer's configured location is used as a hint when the device is registered. Existing setups are unaffected because `suggested_area` only influences a device's area on registration; once the device exists, HA does not re-assign the area. New users get the location string proposed as an area on registration (HA matches an existing area or creates one). The dropped sensor was just merged in #169312, was default-disabled, and few users will have enabled it. Note for posterity: `DeviceEntry.suggested_area` is being deprecated in HA 2026.9 (https://developers.home-assistant.io/blog/2025/08/01/suggested-area-removed-from-deviceentry/), but setting `suggested_area` via `DeviceInfo` is still supported and still influences device area on registration. Only reading it back from `DeviceEntry` is being removed. Co-Authored-By: Claude Opus 4.7 (1M context) --- homeassistant/components/prusalink/entity.py | 1 + homeassistant/components/prusalink/icons.json | 3 --- homeassistant/components/prusalink/sensor.py | 7 ------- homeassistant/components/prusalink/strings.json | 3 --- tests/components/prusalink/test_init.py | 16 ++++++++++++++-- tests/components/prusalink/test_sensor.py | 14 ++++---------- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/prusalink/entity.py b/homeassistant/components/prusalink/entity.py index b24f6ed9f8cd7d..403127dcffb61f 100644 --- a/homeassistant/components/prusalink/entity.py +++ b/homeassistant/components/prusalink/entity.py @@ -25,4 +25,5 @@ def device_info(self) -> DeviceInfo: serial_number=info_data.get("serial"), sw_version=version_data.get("firmware"), configuration_url=self.coordinator.api.client.host, + suggested_area=info_data.get("location"), ) diff --git a/homeassistant/components/prusalink/icons.json b/homeassistant/components/prusalink/icons.json index e368a87e87ee4e..d2b956f10ec249 100644 --- a/homeassistant/components/prusalink/icons.json +++ b/homeassistant/components/prusalink/icons.json @@ -15,9 +15,6 @@ "filename": { "default": "mdi:file-image-outline" }, - "location": { - "default": "mdi:map-marker" - }, "material": { "default": "mdi:palette-swatch-variant" }, diff --git a/homeassistant/components/prusalink/sensor.py b/homeassistant/components/prusalink/sensor.py index 1a6af4da1edd80..580a9380a8ad46 100644 --- a/homeassistant/components/prusalink/sensor.py +++ b/homeassistant/components/prusalink/sensor.py @@ -215,13 +215,6 @@ class PrusaLinkSensorEntityDescription( value_fn=lambda data: cast(str, data["nozzle_diameter"]), entity_registry_enabled_default=False, ), - PrusaLinkSensorEntityDescription[PrinterInfo]( - key="info.location", - translation_key="location", - value_fn=lambda data: cast(str, data["location"]), - supported_fn=lambda data: data.get("location") is not None, - entity_registry_enabled_default=False, - ), PrusaLinkSensorEntityDescription[PrinterInfo]( key="info.min_extrusion_temp", translation_key="min_extrusion_temp", diff --git a/homeassistant/components/prusalink/strings.json b/homeassistant/components/prusalink/strings.json index c9ae97f9d8d445..aab099813e1baf 100644 --- a/homeassistant/components/prusalink/strings.json +++ b/homeassistant/components/prusalink/strings.json @@ -54,9 +54,6 @@ "heatbed_temperature": { "name": "Heatbed temperature" }, - "location": { - "name": "Location" - }, "material": { "name": "Material" }, diff --git a/tests/components/prusalink/test_init.py b/tests/components/prusalink/test_init.py index de5415dcb6c4d6..0a49bfb8cf30e9 100644 --- a/tests/components/prusalink/test_init.py +++ b/tests/components/prusalink/test_init.py @@ -12,7 +12,11 @@ from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant -from homeassistant.helpers import device_registry as dr, issue_registry as ir +from homeassistant.helpers import ( + area_registry as ar, + device_registry as dr, + issue_registry as ir, +) from homeassistant.util.dt import utcnow from tests.common import MockConfigEntry, async_fire_time_changed @@ -24,8 +28,9 @@ async def test_device_info( hass: HomeAssistant, mock_config_entry: MockConfigEntry, device_registry: dr.DeviceRegistry, + area_registry: ar.AreaRegistry, ) -> None: - """Test device info is populated with serial number and firmware version.""" + """Test device info is populated with serial number, firmware, and suggested area.""" assert await hass.config_entries.async_setup(mock_config_entry.entry_id) device = device_registry.async_get_device( @@ -35,6 +40,13 @@ async def test_device_info( assert device.serial_number == "serial-1337" assert device.sw_version == "6.1.2+11023" + # `location` from /api/v1/info is set as suggested_area; the device gets + # placed in that area (created on the fly when not pre-existing). + assert device.area_id is not None + area = area_registry.async_get_area(device.area_id) + assert area is not None + assert area.name == "Workshop" + async def test_unloading( hass: HomeAssistant, diff --git a/tests/components/prusalink/test_sensor.py b/tests/components/prusalink/test_sensor.py index 28fb267890db29..2ea7a862457633 100644 --- a/tests/components/prusalink/test_sensor.py +++ b/tests/components/prusalink/test_sensor.py @@ -335,16 +335,12 @@ async def test_axis_x_y_not_created_when_absent( @pytest.mark.usefixtures("entity_registry_enabled_by_default") -async def test_location_and_min_extrusion_temp_sensors( +async def test_min_extrusion_temp_sensor( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_api: None ) -> None: - """Test location and minimum extrusion temperature sensors from info endpoint.""" + """Test minimum extrusion temperature sensor from info endpoint.""" assert await async_setup_component(hass, "prusalink", {}) - state = hass.states.get("sensor.mock_title_location") - assert state is not None - assert state.state == "Workshop" - state = hass.states.get("sensor.mock_title_minimum_extrusion_temperature") assert state is not None assert state.state == "170" @@ -354,16 +350,14 @@ async def test_location_and_min_extrusion_temp_sensors( @pytest.mark.usefixtures("entity_registry_enabled_by_default") -async def test_location_and_min_extrusion_temp_not_created_when_absent( +async def test_min_extrusion_temp_not_created_when_absent( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_api: None, mock_info_api: dict[str, Any], ) -> None: - """Location and min extrusion temp sensors are not created when info fields are absent.""" - del mock_info_api["location"] + """Min extrusion temp sensor is not created when the info field is absent.""" del mock_info_api["min_extrusion_temp"] assert await async_setup_component(hass, "prusalink", {}) - assert hass.states.get("sensor.mock_title_location") is None assert hass.states.get("sensor.mock_title_minimum_extrusion_temperature") is None