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:
committed by
GitHub
parent
934c0e3c4c
commit
e38ae47e76
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user