1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Add language and location selector to OpenWeatherMap config flow (#153645)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Christopher Fenner
2025-10-07 11:06:04 +02:00
committed by GitHub
parent 934c0e3c4c
commit e38ae47e76
9 changed files with 132 additions and 104 deletions
@@ -8,7 +8,7 @@ import logging
from pyopenweathermap import create_owm_client
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LANGUAGE, CONF_MODE, CONF_NAME
from homeassistant.const import CONF_API_KEY, CONF_LANGUAGE, CONF_MODE
from homeassistant.core import HomeAssistant
from .const import CONFIG_FLOW_VERSION, DEFAULT_OWM_MODE, OWM_MODES, PLATFORMS
@@ -25,7 +25,6 @@ type OpenweathermapConfigEntry = ConfigEntry[OpenweathermapData]
class OpenweathermapData:
"""Runtime data definition."""
name: str
mode: str
coordinator: OWMUpdateCoordinator
@@ -34,7 +33,6 @@ async def async_setup_entry(
hass: HomeAssistant, entry: OpenweathermapConfigEntry
) -> bool:
"""Set up OpenWeatherMap as config entry."""
name = entry.data[CONF_NAME]
api_key = entry.data[CONF_API_KEY]
language = entry.options[CONF_LANGUAGE]
mode = entry.options[CONF_MODE]
@@ -51,7 +49,7 @@ async def async_setup_entry(
entry.async_on_unload(entry.add_update_listener(async_update_options))
entry.runtime_data = OpenweathermapData(name, mode, owm_coordinator)
entry.runtime_data = OpenweathermapData(mode, owm_coordinator)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -14,12 +14,17 @@ from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_LATITUDE,
CONF_LOCATION,
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
LanguageSelector,
LanguageSelectorConfig,
LocationSelector,
LocationSelectorConfig,
)
from .const import (
CONFIG_FLOW_VERSION,
@@ -34,10 +39,12 @@ from .utils import build_data_and_options, validate_api_key
USER_SCHEMA = vol.Schema(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGES),
vol.Required(CONF_LOCATION): LocationSelector(
LocationSelectorConfig(radius=False)
),
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): LanguageSelector(
LanguageSelectorConfig(languages=LANGUAGES, native_name=True)
),
vol.Required(CONF_API_KEY): str,
vol.Optional(CONF_MODE, default=DEFAULT_OWM_MODE): vol.In(OWM_MODES),
}
@@ -45,7 +52,9 @@ USER_SCHEMA = vol.Schema(
OPTIONS_SCHEMA = vol.Schema(
{
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGES),
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): LanguageSelector(
LanguageSelectorConfig(languages=LANGUAGES, native_name=True)
),
vol.Optional(CONF_MODE, default=DEFAULT_OWM_MODE): vol.In(OWM_MODES),
}
)
@@ -70,8 +79,8 @@ class OpenWeatherMapConfigFlow(ConfigFlow, domain=DOMAIN):
description_placeholders = {}
if user_input is not None:
latitude = user_input[CONF_LATITUDE]
longitude = user_input[CONF_LONGITUDE]
latitude = user_input[CONF_LOCATION][CONF_LATITUDE]
longitude = user_input[CONF_LOCATION][CONF_LONGITUDE]
mode = user_input[CONF_MODE]
await self.async_set_unique_id(f"{latitude}-{longitude}")
@@ -82,15 +91,21 @@ class OpenWeatherMapConfigFlow(ConfigFlow, domain=DOMAIN):
)
if not errors:
# Flatten location
location = user_input.pop(CONF_LOCATION)
user_input[CONF_LATITUDE] = location[CONF_LATITUDE]
user_input[CONF_LONGITUDE] = location[CONF_LONGITUDE]
data, options = build_data_and_options(user_input)
return self.async_create_entry(
title=user_input[CONF_NAME], data=data, options=options
title=DEFAULT_NAME, data=data, options=options
)
schema_data = user_input
else:
schema_data = {
CONF_LATITUDE: self.hass.config.latitude,
CONF_LONGITUDE: self.hass.config.longitude,
CONF_LOCATION: {
CONF_LATITUDE: self.hass.config.latitude,
CONF_LONGITUDE: self.hass.config.longitude,
},
CONF_LANGUAGE: self.hass.config.language,
}
@@ -229,7 +229,6 @@ async def async_setup_entry(
) -> None:
"""Set up OpenWeatherMap sensor entities based on a config entry."""
domain_data = config_entry.runtime_data
name = domain_data.name
unique_id = config_entry.unique_id
assert unique_id is not None
coordinator = domain_data.coordinator
@@ -244,7 +243,6 @@ async def async_setup_entry(
elif domain_data.mode == OWM_MODE_AIRPOLLUTION:
async_add_entities(
OpenWeatherMapSensor(
name,
unique_id,
description,
coordinator,
@@ -254,7 +252,6 @@ async def async_setup_entry(
else:
async_add_entities(
OpenWeatherMapSensor(
name,
unique_id,
description,
coordinator,
@@ -272,7 +269,6 @@ class AbstractOpenWeatherMapSensor(SensorEntity):
def __init__(
self,
name: str,
unique_id: str,
description: SensorEntityDescription,
coordinator: OWMUpdateCoordinator,
@@ -286,7 +282,6 @@ class AbstractOpenWeatherMapSensor(SensorEntity):
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, unique_id)},
manufacturer=MANUFACTURER,
name=name,
)
@property
@@ -12,16 +12,14 @@
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"language": "[%key:common::config_flow::data::language%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"location": "[%key:common::config_flow::data::location%]",
"mode": "[%key:common::config_flow::data::mode%]",
"name": "[%key:common::config_flow::data::name%]"
},
"data_description": {
"api_key": "API key for the OpenWeatherMap integration",
"language": "Language for the OpenWeatherMap content",
"latitude": "Latitude of the location",
"longitude": "Longitude of the location",
"location": "Location to get the weather data for",
"mode": "Mode for the OpenWeatherMap API",
"name": "Name for this OpenWeatherMap location"
},
@@ -57,14 +57,13 @@ async def async_setup_entry(
) -> None:
"""Set up OpenWeatherMap weather entity based on a config entry."""
domain_data = config_entry.runtime_data
name = domain_data.name
mode = domain_data.mode
if mode != OWM_MODE_AIRPOLLUTION:
weather_coordinator = domain_data.coordinator
unique_id = f"{config_entry.unique_id}"
owm_weather = OpenWeatherMapWeather(name, unique_id, mode, weather_coordinator)
owm_weather = OpenWeatherMapWeather(unique_id, mode, weather_coordinator)
async_add_entities([owm_weather], False)
@@ -93,7 +92,6 @@ class OpenWeatherMapWeather(SingleCoordinatorWeatherEntity[OWMUpdateCoordinator]
def __init__(
self,
name: str,
unique_id: str,
mode: str,
weather_coordinator: OWMUpdateCoordinator,
@@ -105,7 +103,6 @@ class OpenWeatherMapWeather(SingleCoordinatorWeatherEntity[OWMUpdateCoordinator]
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, unique_id)},
manufacturer=MANUFACTURER,
name=name,
)
self.mode = mode
+6 -3
View File
@@ -17,14 +17,17 @@ from pyopenweathermap import (
from pyopenweathermap.client.owm_abstract_client import OWMClient
import pytest
from homeassistant.components.openweathermap.const import DEFAULT_LANGUAGE, DOMAIN
from homeassistant.components.openweathermap.const import (
DEFAULT_LANGUAGE,
DEFAULT_NAME,
DOMAIN,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
)
from tests.common import MockConfigEntry, patch
@@ -50,7 +53,6 @@ def mock_config_entry(mode: str) -> MockConfigEntry:
CONF_API_KEY: API_KEY,
CONF_LATITUDE: LATITUDE,
CONF_LONGITUDE: LONGITUDE,
CONF_NAME: NAME,
},
options={
CONF_MODE: mode,
@@ -59,6 +61,7 @@ def mock_config_entry(mode: str) -> MockConfigEntry:
entry_id="test",
version=5,
unique_id=f"{LATITUDE}-{LONGITUDE}",
title=DEFAULT_NAME,
)
@@ -41,7 +41,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'aqi',
'friendly_name': 'openweathermap Air quality index',
'friendly_name': 'OpenWeatherMap Air quality index',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -94,7 +94,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'carbon_monoxide',
'friendly_name': 'openweathermap Carbon monoxide',
'friendly_name': 'OpenWeatherMap Carbon monoxide',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -148,7 +148,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'nitrogen_dioxide',
'friendly_name': 'openweathermap Nitrogen dioxide',
'friendly_name': 'OpenWeatherMap Nitrogen dioxide',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -202,7 +202,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'nitrogen_monoxide',
'friendly_name': 'openweathermap Nitrogen monoxide',
'friendly_name': 'OpenWeatherMap Nitrogen monoxide',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -256,7 +256,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'ozone',
'friendly_name': 'openweathermap Ozone',
'friendly_name': 'OpenWeatherMap Ozone',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -310,7 +310,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'pm10',
'friendly_name': 'openweathermap PM10',
'friendly_name': 'OpenWeatherMap PM10',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -364,7 +364,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'pm25',
'friendly_name': 'openweathermap PM2.5',
'friendly_name': 'OpenWeatherMap PM2.5',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -418,7 +418,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'sulphur_dioxide',
'friendly_name': 'openweathermap Sulphur dioxide',
'friendly_name': 'OpenWeatherMap Sulphur dioxide',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
}),
@@ -471,7 +471,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Cloud coverage',
'friendly_name': 'OpenWeatherMap Cloud coverage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -522,7 +522,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Condition',
'friendly_name': 'OpenWeatherMap Condition',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_condition',
@@ -577,7 +577,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Dew Point',
'friendly_name': 'OpenWeatherMap Dew Point',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -634,7 +634,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Feels like temperature',
'friendly_name': 'OpenWeatherMap Feels like temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -688,7 +688,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'humidity',
'friendly_name': 'openweathermap Humidity',
'friendly_name': 'OpenWeatherMap Humidity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -739,7 +739,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Precipitation kind',
'friendly_name': 'OpenWeatherMap Precipitation kind',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_precipitation_kind',
@@ -794,7 +794,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'pressure',
'friendly_name': 'openweathermap Pressure',
'friendly_name': 'OpenWeatherMap Pressure',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
}),
@@ -851,7 +851,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'precipitation_intensity',
'friendly_name': 'openweathermap Rain',
'friendly_name': 'OpenWeatherMap Rain',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
}),
@@ -908,7 +908,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'precipitation_intensity',
'friendly_name': 'openweathermap Snow',
'friendly_name': 'OpenWeatherMap Snow',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
}),
@@ -965,7 +965,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Temperature',
'friendly_name': 'OpenWeatherMap Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -1018,7 +1018,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap UV Index',
'friendly_name': 'OpenWeatherMap UV Index',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'UV index',
}),
@@ -1075,7 +1075,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'distance',
'friendly_name': 'openweathermap Visibility',
'friendly_name': 'OpenWeatherMap Visibility',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
}),
@@ -1126,7 +1126,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Weather',
'friendly_name': 'OpenWeatherMap Weather',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_weather',
@@ -1175,7 +1175,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Weather Code',
'friendly_name': 'OpenWeatherMap Weather Code',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_weather_code',
@@ -1227,7 +1227,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_direction',
'friendly_name': 'openweathermap Wind bearing',
'friendly_name': 'OpenWeatherMap Wind bearing',
'state_class': <SensorStateClass.MEASUREMENT_ANGLE: 'measurement_angle'>,
'unit_of_measurement': '°',
}),
@@ -1287,7 +1287,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_speed',
'friendly_name': 'openweathermap Wind gust',
'friendly_name': 'OpenWeatherMap Wind gust',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
@@ -1347,7 +1347,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_speed',
'friendly_name': 'openweathermap Wind speed',
'friendly_name': 'OpenWeatherMap Wind speed',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
@@ -1400,7 +1400,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Cloud coverage',
'friendly_name': 'OpenWeatherMap Cloud coverage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -1451,7 +1451,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Condition',
'friendly_name': 'OpenWeatherMap Condition',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_condition',
@@ -1506,7 +1506,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Dew Point',
'friendly_name': 'OpenWeatherMap Dew Point',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -1563,7 +1563,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Feels like temperature',
'friendly_name': 'OpenWeatherMap Feels like temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -1617,7 +1617,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'humidity',
'friendly_name': 'openweathermap Humidity',
'friendly_name': 'OpenWeatherMap Humidity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -1668,7 +1668,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Precipitation kind',
'friendly_name': 'OpenWeatherMap Precipitation kind',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_precipitation_kind',
@@ -1723,7 +1723,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'pressure',
'friendly_name': 'openweathermap Pressure',
'friendly_name': 'OpenWeatherMap Pressure',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
}),
@@ -1780,7 +1780,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'precipitation_intensity',
'friendly_name': 'openweathermap Rain',
'friendly_name': 'OpenWeatherMap Rain',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
}),
@@ -1837,7 +1837,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'precipitation_intensity',
'friendly_name': 'openweathermap Snow',
'friendly_name': 'OpenWeatherMap Snow',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
}),
@@ -1894,7 +1894,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'temperature',
'friendly_name': 'openweathermap Temperature',
'friendly_name': 'OpenWeatherMap Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -1947,7 +1947,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap UV Index',
'friendly_name': 'OpenWeatherMap UV Index',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'UV index',
}),
@@ -2004,7 +2004,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'distance',
'friendly_name': 'openweathermap Visibility',
'friendly_name': 'OpenWeatherMap Visibility',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
}),
@@ -2055,7 +2055,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Weather',
'friendly_name': 'OpenWeatherMap Weather',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_weather',
@@ -2104,7 +2104,7 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'friendly_name': 'openweathermap Weather Code',
'friendly_name': 'OpenWeatherMap Weather Code',
}),
'context': <ANY>,
'entity_id': 'sensor.openweathermap_weather_code',
@@ -2156,7 +2156,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_direction',
'friendly_name': 'openweathermap Wind bearing',
'friendly_name': 'OpenWeatherMap Wind bearing',
'state_class': <SensorStateClass.MEASUREMENT_ANGLE: 'measurement_angle'>,
'unit_of_measurement': '°',
}),
@@ -2216,7 +2216,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_speed',
'friendly_name': 'openweathermap Wind gust',
'friendly_name': 'OpenWeatherMap Wind gust',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
@@ -2276,7 +2276,7 @@
'attributes': ReadOnlyDict({
'attribution': 'Data provided by OpenWeatherMap',
'device_class': 'wind_speed',
'friendly_name': 'openweathermap Wind speed',
'friendly_name': 'OpenWeatherMap Wind speed',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
@@ -65,7 +65,7 @@
'attribution': 'Data provided by OpenWeatherMap',
'cloud_coverage': 75,
'dew_point': 4.0,
'friendly_name': 'openweathermap',
'friendly_name': 'OpenWeatherMap',
'humidity': 82,
'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
'pressure': 1000.0,
@@ -129,7 +129,7 @@
'attribution': 'Data provided by OpenWeatherMap',
'cloud_coverage': 75,
'dew_point': 4.0,
'friendly_name': 'openweathermap',
'friendly_name': 'OpenWeatherMap',
'humidity': 82,
'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
'pressure': 1000.0,
@@ -194,7 +194,7 @@
'attribution': 'Data provided by OpenWeatherMap',
'cloud_coverage': 75,
'dew_point': 4.0,
'friendly_name': 'openweathermap',
'friendly_name': 'OpenWeatherMap',
'humidity': 82,
'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
'pressure': 1000.0,
@@ -7,6 +7,7 @@ import pytest
from homeassistant.components.openweathermap.const import (
DEFAULT_LANGUAGE,
DEFAULT_NAME,
DEFAULT_OWM_MODE,
DOMAIN,
OWM_MODE_V30,
@@ -16,9 +17,9 @@ from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_LATITUDE,
CONF_LOCATION,
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@@ -28,7 +29,6 @@ from .conftest import LATITUDE, LONGITUDE
from tests.common import MockConfigEntry
CONFIG = {
CONF_NAME: "openweathermap",
CONF_API_KEY: "foo",
CONF_LATITUDE: LATITUDE,
CONF_LONGITUDE: LONGITUDE,
@@ -36,6 +36,13 @@ CONFIG = {
CONF_MODE: OWM_MODE_V30,
}
USER_INPUT = {
CONF_API_KEY: "foo",
CONF_LOCATION: {CONF_LATITUDE: LATITUDE, CONF_LONGITUDE: LONGITUDE},
CONF_LANGUAGE: DEFAULT_LANGUAGE,
CONF_MODE: OWM_MODE_V30,
}
VALID_YAML_CONFIG = {CONF_API_KEY: "foo"}
@@ -47,31 +54,32 @@ async def test_successful_config_flow(
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
# create entry
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == DEFAULT_NAME
assert result["data"][CONF_LATITUDE] == USER_INPUT[CONF_LOCATION][CONF_LATITUDE]
assert result["data"][CONF_LONGITUDE] == USER_INPUT[CONF_LOCATION][CONF_LONGITUDE]
assert result["data"][CONF_API_KEY] == USER_INPUT[CONF_API_KEY]
# validate entry state
conf_entries = hass.config_entries.async_entries(DOMAIN)
entry = conf_entries[0]
assert entry.state is ConfigEntryState.LOADED
# unload entry
await hass.config_entries.async_unload(conf_entries[0].entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.NOT_LOADED
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == CONFIG[CONF_NAME]
assert result["data"][CONF_LATITUDE] == CONFIG[CONF_LATITUDE]
assert result["data"][CONF_LONGITUDE] == CONFIG[CONF_LONGITUDE]
assert result["data"][CONF_API_KEY] == CONFIG[CONF_API_KEY]
@pytest.mark.parametrize("mode", [OWM_MODE_V30], indirect=True)
async def test_abort_config_flow(
@@ -84,13 +92,14 @@ async def test_abort_config_flow(
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(result["flow_id"], CONFIG)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
assert result["type"] is FlowResultType.ABORT
@@ -156,19 +165,26 @@ async def test_form_invalid_api_key(
owm_client_mock: AsyncMock,
) -> None:
"""Test that the form is served with no input."""
owm_client_mock.validate_key.return_value = False
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
# invalid api key
owm_client_mock.validate_key.return_value = False
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "invalid_api_key"}
# valid api key
owm_client_mock.validate_key.return_value = True
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
result["flow_id"],
USER_INPUT,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
@@ -177,17 +193,23 @@ async def test_form_api_call_error(
owm_client_mock: AsyncMock,
) -> None:
"""Test setting up with api call error."""
owm_client_mock.validate_key.side_effect = RequestError("oops")
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
# simulate api call error
owm_client_mock.validate_key.side_effect = RequestError("oops")
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
# simulate successful api call
owm_client_mock.validate_key.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
result["flow_id"],
USER_INPUT,
)
assert result["type"] is FlowResultType.CREATE_ENTRY