diff --git a/homeassistant/components/purpleair/__init__.py b/homeassistant/components/purpleair/__init__.py index c90f4c9031c..f5c4090dc87 100644 --- a/homeassistant/components/purpleair/__init__.py +++ b/homeassistant/components/purpleair/__init__.py @@ -1,6 +1,9 @@ """The PurpleAir integration.""" from __future__ import annotations +from collections.abc import Mapping +from typing import Any + from aiopurpleair.models.sensors import SensorModel from homeassistant.config_entries import ConfigEntry @@ -9,7 +12,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN +from .const import CONF_SHOW_ON_MAP, DOMAIN from .coordinator import PurpleAirDataUpdateCoordinator PLATFORMS = [Platform.SENSOR] @@ -60,16 +63,30 @@ class PurpleAirEntity(CoordinatorEntity[PurpleAirDataUpdateCoordinator]): self._attr_device_info = DeviceInfo( configuration_url=self.coordinator.async_get_map_url(sensor_index), hw_version=self.sensor_data.hardware, - identifiers={(DOMAIN, str(self._sensor_index))}, + identifiers={(DOMAIN, str(sensor_index))}, manufacturer="PurpleAir, Inc.", model=self.sensor_data.model, name=self.sensor_data.name, sw_version=self.sensor_data.firmware_version, ) - self._attr_extra_state_attributes = { - ATTR_LATITUDE: self.sensor_data.latitude, - ATTR_LONGITUDE: self.sensor_data.longitude, - } + self._entry = entry + + @property + def extra_state_attributes(self) -> Mapping[str, Any]: + """Return entity specific state attributes.""" + attrs = {} + + # Displaying the geography on the map relies upon putting the latitude/longitude + # in the entity attributes with "latitude" and "longitude" as the keys. + # Conversely, we can hide the location on the map by using other keys, like + # "lati" and "long": + if self._entry.options.get(CONF_SHOW_ON_MAP): + attrs[ATTR_LATITUDE] = self.sensor_data.latitude + attrs[ATTR_LONGITUDE] = self.sensor_data.longitude + else: + attrs["lati"] = self.sensor_data.latitude + attrs["long"] = self.sensor_data.longitude + return attrs @property def sensor_data(self) -> SensorModel: diff --git a/homeassistant/components/purpleair/config_flow.py b/homeassistant/components/purpleair/config_flow.py index 604bcb28c0e..c7988c02e6a 100644 --- a/homeassistant/components/purpleair/config_flow.py +++ b/homeassistant/components/purpleair/config_flow.py @@ -31,7 +31,7 @@ from homeassistant.helpers.selector import ( SelectSelectorMode, ) -from .const import CONF_SENSOR_INDICES, DOMAIN, LOGGER +from .const import CONF_SENSOR_INDICES, CONF_SHOW_ON_MAP, DOMAIN, LOGGER CONF_DISTANCE = "distance" CONF_NEARBY_SENSOR_OPTIONS = "nearby_sensor_options" @@ -318,6 +318,22 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow): self._flow_data: dict[str, Any] = {} self.config_entry = config_entry + @property + def settings_schema(self) -> vol.Schema: + """Return the settings schema.""" + return vol.Schema( + { + vol.Optional( + CONF_SHOW_ON_MAP, + description={ + "suggested_value": self.config_entry.options.get( + CONF_SHOW_ON_MAP + ) + }, + ): bool + } + ) + async def async_step_add_sensor( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -352,7 +368,7 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow): async def async_step_choose_sensor( self, user_input: dict[str, Any] | None = None ) -> FlowResult: - """Handle the selection of a sensor.""" + """Choose a sensor.""" if user_input is None: options = self._flow_data.pop(CONF_NEARBY_SENSOR_OPTIONS) return self.async_show_form( @@ -375,13 +391,13 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow): """Manage the options.""" return self.async_show_menu( step_id="init", - menu_options=["add_sensor", "remove_sensor"], + menu_options=["add_sensor", "remove_sensor", "settings"], ) async def async_step_remove_sensor( self, user_input: dict[str, Any] | None = None ) -> FlowResult: - """Add a sensor.""" + """Remove a sensor.""" if user_input is None: return self.async_show_form( step_id="remove_sensor", @@ -437,3 +453,15 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow): options[CONF_SENSOR_INDICES].remove(removed_sensor_index) return self.async_create_entry(data=options) + + async def async_step_settings( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage settings.""" + if user_input is None: + return self.async_show_form( + step_id="settings", data_schema=self.settings_schema + ) + + options = deepcopy({**self.config_entry.options}) + return self.async_create_entry(data=options | user_input) diff --git a/homeassistant/components/purpleair/const.py b/homeassistant/components/purpleair/const.py index 60f51a9e7dd..e3ea7807a21 100644 --- a/homeassistant/components/purpleair/const.py +++ b/homeassistant/components/purpleair/const.py @@ -7,3 +7,4 @@ LOGGER = logging.getLogger(__package__) CONF_READ_KEY = "read_key" CONF_SENSOR_INDICES = "sensor_indices" +CONF_SHOW_ON_MAP = "show_on_map" diff --git a/homeassistant/components/purpleair/strings.json b/homeassistant/components/purpleair/strings.json index 3d18fef3906..836496d0ca8 100644 --- a/homeassistant/components/purpleair/strings.json +++ b/homeassistant/components/purpleair/strings.json @@ -79,7 +79,8 @@ "init": { "menu_options": { "add_sensor": "Add sensor", - "remove_sensor": "Remove sensor" + "remove_sensor": "Remove sensor", + "settings": "Settings" } }, "remove_sensor": { @@ -90,6 +91,12 @@ "data_description": { "sensor_device_id": "The sensor to remove" } + }, + "settings": { + "title": "Settings", + "data": { + "show_on_map": "Show configured sensor locations on the map" + } } }, "error": { diff --git a/tests/components/purpleair/test_config_flow.py b/tests/components/purpleair/test_config_flow.py index ce911183dfd..503ba23e052 100644 --- a/tests/components/purpleair/test_config_flow.py +++ b/tests/components/purpleair/test_config_flow.py @@ -302,3 +302,29 @@ async def test_options_remove_sensor( # Unload to make sure the update does not run after the # mock is removed. await hass.config_entries.async_unload(config_entry.entry_id) + + +async def test_options_settings( + hass: HomeAssistant, config_entry, setup_config_entry +) -> None: + """Test setting settings via the options flow.""" + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={"next_step_id": "settings"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "settings" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={"show_on_map": True} + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "sensor_indices": [TEST_SENSOR_INDEX1], + "show_on_map": True, + } + + assert config_entry.options["show_on_map"] is True