mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add ability to configure map icons for PurpleAir (#86124)
This commit is contained in:
parent
522d2496df
commit
39e0662fc8
@ -1,6 +1,9 @@
|
|||||||
"""The PurpleAir integration."""
|
"""The PurpleAir integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from aiopurpleair.models.sensors import SensorModel
|
from aiopurpleair.models.sensors import SensorModel
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -9,7 +12,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import CONF_SHOW_ON_MAP, DOMAIN
|
||||||
from .coordinator import PurpleAirDataUpdateCoordinator
|
from .coordinator import PurpleAirDataUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.SENSOR]
|
PLATFORMS = [Platform.SENSOR]
|
||||||
@ -60,16 +63,30 @@ class PurpleAirEntity(CoordinatorEntity[PurpleAirDataUpdateCoordinator]):
|
|||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
configuration_url=self.coordinator.async_get_map_url(sensor_index),
|
configuration_url=self.coordinator.async_get_map_url(sensor_index),
|
||||||
hw_version=self.sensor_data.hardware,
|
hw_version=self.sensor_data.hardware,
|
||||||
identifiers={(DOMAIN, str(self._sensor_index))},
|
identifiers={(DOMAIN, str(sensor_index))},
|
||||||
manufacturer="PurpleAir, Inc.",
|
manufacturer="PurpleAir, Inc.",
|
||||||
model=self.sensor_data.model,
|
model=self.sensor_data.model,
|
||||||
name=self.sensor_data.name,
|
name=self.sensor_data.name,
|
||||||
sw_version=self.sensor_data.firmware_version,
|
sw_version=self.sensor_data.firmware_version,
|
||||||
)
|
)
|
||||||
self._attr_extra_state_attributes = {
|
self._entry = entry
|
||||||
ATTR_LATITUDE: self.sensor_data.latitude,
|
|
||||||
ATTR_LONGITUDE: self.sensor_data.longitude,
|
@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
|
@property
|
||||||
def sensor_data(self) -> SensorModel:
|
def sensor_data(self) -> SensorModel:
|
||||||
|
@ -31,7 +31,7 @@ from homeassistant.helpers.selector import (
|
|||||||
SelectSelectorMode,
|
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_DISTANCE = "distance"
|
||||||
CONF_NEARBY_SENSOR_OPTIONS = "nearby_sensor_options"
|
CONF_NEARBY_SENSOR_OPTIONS = "nearby_sensor_options"
|
||||||
@ -318,6 +318,22 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self._flow_data: dict[str, Any] = {}
|
self._flow_data: dict[str, Any] = {}
|
||||||
self.config_entry = config_entry
|
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(
|
async def async_step_add_sensor(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
@ -352,7 +368,7 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
async def async_step_choose_sensor(
|
async def async_step_choose_sensor(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle the selection of a sensor."""
|
"""Choose a sensor."""
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
options = self._flow_data.pop(CONF_NEARBY_SENSOR_OPTIONS)
|
options = self._flow_data.pop(CONF_NEARBY_SENSOR_OPTIONS)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -375,13 +391,13 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
"""Manage the options."""
|
"""Manage the options."""
|
||||||
return self.async_show_menu(
|
return self.async_show_menu(
|
||||||
step_id="init",
|
step_id="init",
|
||||||
menu_options=["add_sensor", "remove_sensor"],
|
menu_options=["add_sensor", "remove_sensor", "settings"],
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_remove_sensor(
|
async def async_step_remove_sensor(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Add a sensor."""
|
"""Remove a sensor."""
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="remove_sensor",
|
step_id="remove_sensor",
|
||||||
@ -437,3 +453,15 @@ class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
options[CONF_SENSOR_INDICES].remove(removed_sensor_index)
|
options[CONF_SENSOR_INDICES].remove(removed_sensor_index)
|
||||||
|
|
||||||
return self.async_create_entry(data=options)
|
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)
|
||||||
|
@ -7,3 +7,4 @@ LOGGER = logging.getLogger(__package__)
|
|||||||
|
|
||||||
CONF_READ_KEY = "read_key"
|
CONF_READ_KEY = "read_key"
|
||||||
CONF_SENSOR_INDICES = "sensor_indices"
|
CONF_SENSOR_INDICES = "sensor_indices"
|
||||||
|
CONF_SHOW_ON_MAP = "show_on_map"
|
||||||
|
@ -79,7 +79,8 @@
|
|||||||
"init": {
|
"init": {
|
||||||
"menu_options": {
|
"menu_options": {
|
||||||
"add_sensor": "Add sensor",
|
"add_sensor": "Add sensor",
|
||||||
"remove_sensor": "Remove sensor"
|
"remove_sensor": "Remove sensor",
|
||||||
|
"settings": "Settings"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove_sensor": {
|
"remove_sensor": {
|
||||||
@ -90,6 +91,12 @@
|
|||||||
"data_description": {
|
"data_description": {
|
||||||
"sensor_device_id": "The sensor to remove"
|
"sensor_device_id": "The sensor to remove"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Settings",
|
||||||
|
"data": {
|
||||||
|
"show_on_map": "Show configured sensor locations on the map"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
@ -302,3 +302,29 @@ async def test_options_remove_sensor(
|
|||||||
# Unload to make sure the update does not run after the
|
# Unload to make sure the update does not run after the
|
||||||
# mock is removed.
|
# mock is removed.
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user