mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add back Netatmo public weather sensors (#34401)
* Add public weather sensors back in * Remove stale code * Cleanup after before adding entities * Fix pylint complaint * Add test for options flow * Change mode to listbox * Update .coveragerc * Address comments * Don't process empty list * Address comment * Fix mistake * Make signal unique * Make string more unique * Fix merge conflict
This commit is contained in:
parent
af6a4bb6cf
commit
155a5f7c26
@ -538,6 +538,7 @@ omit =
|
||||
homeassistant/components/netatmo/climate.py
|
||||
homeassistant/components/netatmo/const.py
|
||||
homeassistant/components/netatmo/sensor.py
|
||||
homeassistant/components/netatmo/webhook.py
|
||||
homeassistant/components/netdata/sensor.py
|
||||
homeassistant/components/netgear/device_tracker.py
|
||||
homeassistant/components/netgear_lte/*
|
||||
|
@ -92,6 +92,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
hass, entry
|
||||
)
|
||||
|
||||
# Set unique id if non was set (migration)
|
||||
if not entry.unique_id:
|
||||
hass.config_entries.async_update_entry(entry, unique_id=DOMAIN)
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
AUTH: api.ConfigEntryNetatmoAuth(hass, entry, implementation)
|
||||
}
|
||||
|
@ -1,10 +1,24 @@
|
||||
"""Config flow for Netatmo."""
|
||||
import logging
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import DOMAIN
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_SHOW_ON_MAP
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv
|
||||
|
||||
from .const import (
|
||||
CONF_AREA_NAME,
|
||||
CONF_LAT_NE,
|
||||
CONF_LAT_SW,
|
||||
CONF_LON_NE,
|
||||
CONF_LON_SW,
|
||||
CONF_NEW_AREA,
|
||||
CONF_PUBLIC_MODE,
|
||||
CONF_WEATHER_AREAS,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -17,6 +31,12 @@ class NetatmoFlowHandler(
|
||||
DOMAIN = DOMAIN
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return NetatmoOptionsFlowHandler(config_entry)
|
||||
|
||||
@property
|
||||
def logger(self) -> logging.Logger:
|
||||
"""Return logger."""
|
||||
@ -45,11 +65,113 @@ class NetatmoFlowHandler(
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow start."""
|
||||
if self.hass.config_entries.async_entries(DOMAIN):
|
||||
return self.async_abort(reason="already_setup")
|
||||
|
||||
await self.async_set_unique_id(DOMAIN)
|
||||
return await super().async_step_user(user_input)
|
||||
|
||||
async def async_step_homekit(self, homekit_info):
|
||||
"""Handle HomeKit discovery."""
|
||||
return await self.async_step_user()
|
||||
|
||||
|
||||
class NetatmoOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle Netatmo options."""
|
||||
|
||||
def __init__(self, config_entry: config_entries.ConfigEntry):
|
||||
"""Initialize Netatmo options flow."""
|
||||
self.config_entry = config_entry
|
||||
self.options = dict(config_entry.options)
|
||||
self.options.setdefault(CONF_WEATHER_AREAS, {})
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the Netatmo options."""
|
||||
return await self.async_step_public_weather_areas()
|
||||
|
||||
async def async_step_public_weather_areas(self, user_input=None):
|
||||
"""Manage configuration of Netatmo public weather areas."""
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
new_client = user_input.pop(CONF_NEW_AREA, None)
|
||||
areas = user_input.pop(CONF_WEATHER_AREAS, None)
|
||||
user_input[CONF_WEATHER_AREAS] = {
|
||||
area: self.options[CONF_WEATHER_AREAS][area] for area in areas
|
||||
}
|
||||
self.options.update(user_input)
|
||||
if new_client:
|
||||
return await self.async_step_public_weather(
|
||||
user_input={CONF_NEW_AREA: new_client}
|
||||
)
|
||||
|
||||
return await self._update_options()
|
||||
|
||||
weather_areas = list(self.options[CONF_WEATHER_AREAS])
|
||||
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_WEATHER_AREAS, default=weather_areas,
|
||||
): cv.multi_select(weather_areas),
|
||||
vol.Optional(CONF_NEW_AREA): str,
|
||||
}
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="public_weather_areas", data_schema=data_schema, errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_public_weather(self, user_input=None):
|
||||
"""Manage configuration of Netatmo public weather sensors."""
|
||||
if user_input is not None and CONF_NEW_AREA not in user_input:
|
||||
self.options[CONF_WEATHER_AREAS][user_input[CONF_AREA_NAME]] = user_input
|
||||
return await self.async_step_public_weather_areas()
|
||||
|
||||
orig_options = self.config_entry.options.get(CONF_WEATHER_AREAS, {}).get(
|
||||
user_input[CONF_NEW_AREA], {}
|
||||
)
|
||||
|
||||
default_longitude = self.hass.config.longitude
|
||||
default_latitude = self.hass.config.latitude
|
||||
default_size = 0.04
|
||||
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_AREA_NAME, default=user_input[CONF_NEW_AREA]): str,
|
||||
vol.Optional(
|
||||
CONF_LAT_NE,
|
||||
default=orig_options.get(
|
||||
CONF_LAT_NE, default_latitude + default_size
|
||||
),
|
||||
): cv.latitude,
|
||||
vol.Optional(
|
||||
CONF_LON_NE,
|
||||
default=orig_options.get(
|
||||
CONF_LON_NE, default_longitude + default_size
|
||||
),
|
||||
): cv.longitude,
|
||||
vol.Optional(
|
||||
CONF_LAT_SW,
|
||||
default=orig_options.get(
|
||||
CONF_LAT_SW, default_latitude - default_size
|
||||
),
|
||||
): cv.latitude,
|
||||
vol.Optional(
|
||||
CONF_LON_SW,
|
||||
default=orig_options.get(
|
||||
CONF_LON_SW, default_longitude - default_size
|
||||
),
|
||||
): cv.longitude,
|
||||
vol.Required(
|
||||
CONF_PUBLIC_MODE, default=orig_options.get(CONF_PUBLIC_MODE, "avg"),
|
||||
): vol.In(["avg", "max"]),
|
||||
vol.Required(
|
||||
CONF_SHOW_ON_MAP, default=orig_options.get(CONF_SHOW_ON_MAP, False),
|
||||
): bool,
|
||||
}
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="public_weather", data_schema=data_schema)
|
||||
|
||||
async def _update_options(self):
|
||||
"""Update config entry options."""
|
||||
return self.async_create_entry(
|
||||
title="Netatmo Public Weather", data=self.options
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ MODELS = {
|
||||
"NAModule4": "Smart Additional Indoor module",
|
||||
"NAModule3": "Smart Rain Gauge",
|
||||
"NAModule2": "Smart Anemometer",
|
||||
"public": "Public Weather stations",
|
||||
}
|
||||
|
||||
AUTH = "netatmo_auth"
|
||||
@ -28,6 +29,14 @@ CAMERA_DATA = "netatmo_camera"
|
||||
HOME_DATA = "netatmo_home_data"
|
||||
|
||||
CONF_CLOUDHOOK_URL = "cloudhook_url"
|
||||
CONF_WEATHER_AREAS = "weather_areas"
|
||||
CONF_NEW_AREA = "new_area"
|
||||
CONF_AREA_NAME = "area_name"
|
||||
CONF_LAT_NE = "lat_ne"
|
||||
CONF_LON_NE = "lon_ne"
|
||||
CONF_LAT_SW = "lat_sw"
|
||||
CONF_LON_SW = "lon_sw"
|
||||
CONF_PUBLIC_MODE = "mode"
|
||||
|
||||
OAUTH2_AUTHORIZE = "https://api.netatmo.com/oauth2/authorize"
|
||||
OAUTH2_TOKEN = "https://api.netatmo.com/oauth2/token"
|
||||
|
@ -4,8 +4,12 @@ import logging
|
||||
|
||||
import pyatmo
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_LATITUDE,
|
||||
ATTR_LONGITUDE,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
CONF_SHOW_ON_MAP,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@ -13,24 +17,31 @@ from homeassistant.const import (
|
||||
TEMP_CELSIUS,
|
||||
UNIT_PERCENTAGE,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import async_entries_for_config_entry
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import AUTH, DOMAIN, MANUFACTURER, MODELS
|
||||
from .const import (
|
||||
AUTH,
|
||||
CONF_AREA_NAME,
|
||||
CONF_LAT_NE,
|
||||
CONF_LAT_SW,
|
||||
CONF_LON_NE,
|
||||
CONF_LON_SW,
|
||||
CONF_PUBLIC_MODE,
|
||||
CONF_WEATHER_AREAS,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
MODELS,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_MODULES = "modules"
|
||||
CONF_STATION = "station"
|
||||
CONF_AREAS = "areas"
|
||||
CONF_LAT_NE = "lat_ne"
|
||||
CONF_LON_NE = "lon_ne"
|
||||
CONF_LAT_SW = "lat_sw"
|
||||
CONF_LON_SW = "lon_sw"
|
||||
|
||||
DEFAULT_MODE = "avg"
|
||||
MODE_TYPES = {"max", "avg"}
|
||||
|
||||
# This is the Netatmo data upload interval in seconds
|
||||
NETATMO_UPDATE_INTERVAL = 600
|
||||
|
||||
@ -107,10 +118,15 @@ NETATMO_DEVICE_TYPES = {
|
||||
"HomeCoachData": "home coach",
|
||||
}
|
||||
|
||||
PUBLIC = "public"
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up the Netatmo weather and homecoach platform."""
|
||||
auth = hass.data[DOMAIN][entry.entry_id][AUTH]
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
def find_entities(data):
|
||||
"""Find all entities."""
|
||||
@ -145,6 +161,41 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
||||
|
||||
async_add_entities(await hass.async_add_executor_job(get_entities), True)
|
||||
|
||||
@callback
|
||||
def add_public_entities():
|
||||
"""Retrieve Netatmo public weather entities."""
|
||||
entities = []
|
||||
for area in entry.options.get(CONF_WEATHER_AREAS, {}).values():
|
||||
data = NetatmoPublicData(
|
||||
auth,
|
||||
lat_ne=area[CONF_LAT_NE],
|
||||
lon_ne=area[CONF_LON_NE],
|
||||
lat_sw=area[CONF_LAT_SW],
|
||||
lon_sw=area[CONF_LON_SW],
|
||||
)
|
||||
for sensor_type in SUPPORTED_PUBLIC_SENSOR_TYPES:
|
||||
entities.append(NetatmoPublicSensor(area, data, sensor_type,))
|
||||
|
||||
for device in async_entries_for_config_entry(device_registry, entry.entry_id):
|
||||
if device.model == "Public Weather stations":
|
||||
device_registry.async_remove_device(device.id)
|
||||
|
||||
if entities:
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(
|
||||
hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}", add_public_entities
|
||||
)
|
||||
|
||||
entry.add_update_listener(async_config_entry_updated)
|
||||
|
||||
add_public_entities()
|
||||
|
||||
|
||||
async def async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle signals of config entry being updated."""
|
||||
async_dispatcher_send(hass, f"signal-{DOMAIN}-public-update-{entry.entry_id}")
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Netatmo weather and homecoach platform."""
|
||||
@ -403,20 +454,48 @@ class NetatmoSensor(Entity):
|
||||
return
|
||||
|
||||
|
||||
class NetatmoData:
|
||||
"""Get the latest data from Netatmo."""
|
||||
|
||||
def __init__(self, auth, station_data):
|
||||
"""Initialize the data object."""
|
||||
self.data = {}
|
||||
self.station_data = station_data
|
||||
self.auth = auth
|
||||
|
||||
def get_module_infos(self):
|
||||
"""Return all modules available on the API as a dict."""
|
||||
return self.station_data.getModules()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Call the Netatmo API to update the data."""
|
||||
self.station_data = self.station_data.__class__(self.auth)
|
||||
|
||||
data = self.station_data.lastData(exclude=3600, byId=True)
|
||||
if not data:
|
||||
_LOGGER.debug("No data received when updating station data")
|
||||
return
|
||||
self.data = data
|
||||
|
||||
|
||||
class NetatmoPublicSensor(Entity):
|
||||
"""Represent a single sensor in a Netatmo."""
|
||||
|
||||
def __init__(self, area_name, data, sensor_type, mode):
|
||||
def __init__(self, area, data, sensor_type):
|
||||
"""Initialize the sensor."""
|
||||
self.netatmo_data = data
|
||||
self.type = sensor_type
|
||||
self._mode = mode
|
||||
self._name = f"{MANUFACTURER} {area_name} {SENSOR_TYPES[self.type][0]}"
|
||||
self._area_name = area_name
|
||||
self._mode = area[CONF_PUBLIC_MODE]
|
||||
self._area_name = area[CONF_AREA_NAME]
|
||||
self._name = f"{MANUFACTURER} {self._area_name} {SENSOR_TYPES[self.type][0]}"
|
||||
self._state = None
|
||||
self._device_class = SENSOR_TYPES[self.type][3]
|
||||
self._icon = SENSOR_TYPES[self.type][2]
|
||||
self._unit_of_measurement = SENSOR_TYPES[self.type][1]
|
||||
self._show_on_map = area[CONF_SHOW_ON_MAP]
|
||||
self._unique_id = f"{self._name.replace(' ', '-')}"
|
||||
self._module_type = PUBLIC
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -440,9 +519,24 @@ class NetatmoPublicSensor(Entity):
|
||||
"identifiers": {(DOMAIN, self._area_name)},
|
||||
"name": self._area_name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"model": "public",
|
||||
"model": MODELS[self._module_type],
|
||||
}
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the attributes of the device."""
|
||||
attrs = {}
|
||||
|
||||
if self._show_on_map:
|
||||
attrs[ATTR_LATITUDE] = (
|
||||
self.netatmo_data.lat_ne + self.netatmo_data.lat_sw
|
||||
) / 2
|
||||
attrs[ATTR_LONGITUDE] = (
|
||||
self.netatmo_data.lon_ne + self.netatmo_data.lon_sw
|
||||
) / 2
|
||||
|
||||
return attrs
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
@ -453,6 +547,11 @@ class NetatmoPublicSensor(Entity):
|
||||
"""Return the unit of measurement of this entity."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID for this sensor."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
@ -536,28 +635,3 @@ class NetatmoPublicData:
|
||||
return
|
||||
|
||||
self.data = data
|
||||
|
||||
|
||||
class NetatmoData:
|
||||
"""Get the latest data from Netatmo."""
|
||||
|
||||
def __init__(self, auth, station_data):
|
||||
"""Initialize the data object."""
|
||||
self.data = {}
|
||||
self.station_data = station_data
|
||||
self.auth = auth
|
||||
|
||||
def get_module_infos(self):
|
||||
"""Return all modules available on the API as a dict."""
|
||||
return self.station_data.getModules()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Call the Netatmo API to update the data."""
|
||||
self.station_data = self.station_data.__class__(self.auth)
|
||||
|
||||
data = self.station_data.lastData(exclude=3600, byId=True)
|
||||
if not data:
|
||||
_LOGGER.debug("No data received when updating station data")
|
||||
return
|
||||
self.data = data
|
||||
|
@ -13,5 +13,30 @@
|
||||
"create_entry": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"public_weather": {
|
||||
"data": {
|
||||
"area_name": "Name of the area",
|
||||
"lat_ne": "Latitude North-East corner",
|
||||
"lon_ne": "Longitude North-East corner",
|
||||
"lat_sw": "Latitude South-West corner",
|
||||
"lon_sw": "Longitude South-West corner",
|
||||
"mode": "Calculation",
|
||||
"show_on_map": "Show on map"
|
||||
},
|
||||
"description": "Configure a public weather sensor for an area.",
|
||||
"title": "Netatmo public weather sensor"
|
||||
},
|
||||
"public_weather_areas": {
|
||||
"data": {
|
||||
"new_area": "Area name",
|
||||
"weather_areas": "Weather areas"
|
||||
},
|
||||
"description": "Configure public weather sensors.",
|
||||
"title": "Netatmo public weather sensor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,42 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_setup": "Already configured. Only a single configuration possible.",
|
||||
"authorize_url_timeout": "Timeout generating authorize URL.",
|
||||
"missing_configuration": "The component is not configured. Please follow the documentation."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully authenticated"
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Pick Authentication Method"
|
||||
}
|
||||
}
|
||||
"config": {
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "Pick Authentication Method"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_setup": "Already configured. Only a single configuration possible.",
|
||||
"authorize_url_timeout": "Timeout generating authorize URL.",
|
||||
"missing_configuration": "The component is not configured. Please follow the documentation."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully authenticated"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"public_weather": {
|
||||
"data": {
|
||||
"area_name": "Name of the area",
|
||||
"lat_ne": "Latitude North-East corner",
|
||||
"lon_ne": "Longitude North-East corner",
|
||||
"lat_sw": "Latitude South-West corner",
|
||||
"lon_sw": "Longitude South-West corner",
|
||||
"mode": "Calculation",
|
||||
"show_on_map": "Show on map"
|
||||
},
|
||||
"description": "Configure a public weather sensor for an area.",
|
||||
"title": "Netatmo public weather sensor"
|
||||
},
|
||||
"public_weather_areas": {
|
||||
"data": {
|
||||
"new_area": "Area name",
|
||||
"weather_areas": "Weather areas"
|
||||
},
|
||||
"description": "Configure public weather sensors.",
|
||||
"title": "Netatmo public weather sensor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,8 @@ async def handle_webhook(hass, webhook_id, request):
|
||||
"""Handle webhook callback."""
|
||||
try:
|
||||
data = await request.json()
|
||||
except ValueError:
|
||||
except ValueError as err:
|
||||
_LOGGER.error("Error in data: %s", err)
|
||||
return None
|
||||
|
||||
_LOGGER.debug("Got webhook data: %s", data)
|
||||
@ -36,6 +37,12 @@ async def handle_webhook(hass, webhook_id, request):
|
||||
)
|
||||
for event_data in data.get("event_list"):
|
||||
async_evaluate_event(hass, event_data)
|
||||
elif event_type == "therm_mode":
|
||||
hass.bus.async_fire(
|
||||
event_type=NETATMO_EVENT, event_data={"type": event_type, "data": data}
|
||||
)
|
||||
for event_data in data.get("data"):
|
||||
async_evaluate_event(hass, event_data)
|
||||
else:
|
||||
async_evaluate_event(hass, data)
|
||||
|
||||
@ -58,6 +65,18 @@ def async_evaluate_event(hass, event_data):
|
||||
event_type=NETATMO_EVENT,
|
||||
event_data={"type": event_type, "data": person_event_data},
|
||||
)
|
||||
elif event_type == "therm_mode":
|
||||
_LOGGER.debug("therm_mode: %s", event_data)
|
||||
hass.bus.async_fire(
|
||||
event_type=NETATMO_EVENT,
|
||||
event_data={"type": event_type, "data": event_data},
|
||||
)
|
||||
elif event_type == "set_point":
|
||||
_LOGGER.debug("set_point: %s", event_data)
|
||||
hass.bus.async_fire(
|
||||
event_type=NETATMO_EVENT,
|
||||
event_data={"type": event_type, "data": event_data},
|
||||
)
|
||||
else:
|
||||
hass.bus.async_fire(
|
||||
event_type=NETATMO_EVENT,
|
||||
|
@ -2,6 +2,8 @@
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.netatmo import config_flow
|
||||
from homeassistant.components.netatmo.const import (
|
||||
CONF_NEW_AREA,
|
||||
CONF_WEATHER_AREAS,
|
||||
DOMAIN,
|
||||
OAUTH2_AUTHORIZE,
|
||||
OAUTH2_TOKEN,
|
||||
@ -15,6 +17,8 @@ from tests.common import MockConfigEntry
|
||||
CLIENT_ID = "1234"
|
||||
CLIENT_SECRET = "5678"
|
||||
|
||||
VALID_CONFIG = {}
|
||||
|
||||
|
||||
async def test_abort_if_existing_entry(hass):
|
||||
"""Check flow abort when an entry already exist."""
|
||||
@ -27,7 +31,7 @@ async def test_abort_if_existing_entry(hass):
|
||||
"netatmo", context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_setup"
|
||||
assert result["reason"] == "missing_configuration"
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"netatmo",
|
||||
@ -35,7 +39,7 @@ async def test_abort_if_existing_entry(hass):
|
||||
data={"host": "0.0.0.0", "properties": {"id": "aa:bb:cc:dd:ee:ff"}},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_setup"
|
||||
assert result["reason"] == "missing_configuration"
|
||||
|
||||
|
||||
async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
||||
@ -98,3 +102,47 @@ async def test_full_flow(hass, aiohttp_client, aioclient_mock):
|
||||
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_option_flow(hass):
|
||||
"""Test config flow options."""
|
||||
valid_option = {
|
||||
"lat_ne": 32.91336,
|
||||
"lon_sw": -117.26743,
|
||||
"show_on_map": False,
|
||||
"area_name": "Home",
|
||||
"lon_ne": -117.187429,
|
||||
"lat_sw": 32.83336,
|
||||
"mode": "avg",
|
||||
}
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, unique_id=DOMAIN, data=VALID_CONFIG, options={},
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "public_weather_areas"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={CONF_NEW_AREA: "Home"}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "public_weather"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input=valid_option
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "public_weather_areas"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options == {CONF_WEATHER_AREAS: {"Home": valid_option}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user