mirror of
https://github.com/home-assistant/core.git
synced 2026-04-28 18:12:37 +02:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d81a19b5a2 | |||
| 39ce063500 | |||
| af34d130b6 | |||
| 0cb5ccd492 | |||
| 7433fa9265 | |||
| d629a55134 | |||
| 7a72ada8b2 | |||
| a28646bc24 | |||
| ca4433bd70 | |||
| c61bcbf982 | |||
| e9f398ac28 | |||
| 7417b3be66 | |||
| 99afc17b3f | |||
| cc5fc2baa4 | |||
| e2f0520028 | |||
| 5cb1924290 | |||
| aa176aab07 | |||
| 5695a63e59 |
@@ -3,6 +3,6 @@
|
||||
"name": "Atag",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/atag/",
|
||||
"requirements": ["pyatag==0.3.1.1"],
|
||||
"requirements": ["pyatag==0.3.1.2"],
|
||||
"codeowners": ["@MatsNL"]
|
||||
}
|
||||
|
||||
@@ -15,14 +15,6 @@ from .const import CONF_KEY, CONF_UUID, KEY_IP, KEY_MAC, TIMEOUT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Optional(CONF_KEY): str,
|
||||
vol.Optional(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register("daikin")
|
||||
class FlowHandler(config_entries.ConfigFlow):
|
||||
@@ -31,12 +23,26 @@ class FlowHandler(config_entries.ConfigFlow):
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||
|
||||
def _create_entry(self, host, mac, key=None, uuid=None, password=None):
|
||||
def __init__(self):
|
||||
"""Initialize the Daikin config flow."""
|
||||
self.host = None
|
||||
|
||||
@property
|
||||
def schema(self):
|
||||
"""Return current schema."""
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST, default=self.host): str,
|
||||
vol.Optional(CONF_KEY): str,
|
||||
vol.Optional(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
async def _create_entry(self, host, mac, key=None, uuid=None, password=None):
|
||||
"""Register new entry."""
|
||||
# Check if mac already is registered
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data[KEY_MAC] == mac:
|
||||
return self.async_abort(reason="already_configured")
|
||||
await self.async_set_unique_id(mac)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=host,
|
||||
@@ -73,31 +79,31 @@ class FlowHandler(config_entries.ConfigFlow):
|
||||
except asyncio.TimeoutError:
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=DATA_SCHEMA,
|
||||
data_schema=self.schema,
|
||||
errors={"base": "device_timeout"},
|
||||
)
|
||||
except web_exceptions.HTTPForbidden:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors={"base": "forbidden"},
|
||||
step_id="user", data_schema=self.schema, errors={"base": "forbidden"},
|
||||
)
|
||||
except ClientError:
|
||||
_LOGGER.exception("ClientError")
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors={"base": "device_fail"},
|
||||
step_id="user", data_schema=self.schema, errors={"base": "device_fail"},
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected error creating device")
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors={"base": "device_fail"},
|
||||
step_id="user", data_schema=self.schema, errors={"base": "device_fail"},
|
||||
)
|
||||
|
||||
mac = device.mac
|
||||
return self._create_entry(host, mac, key, uuid, password)
|
||||
return await self._create_entry(host, mac, key, uuid, password)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""User initiated config flow."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA,)
|
||||
return self.async_show_form(step_id="user", data_schema=self.schema)
|
||||
return await self._create_device(
|
||||
user_input[CONF_HOST],
|
||||
user_input.get(CONF_KEY),
|
||||
@@ -111,7 +117,10 @@ class FlowHandler(config_entries.ConfigFlow):
|
||||
return await self.async_step_user()
|
||||
return await self._create_device(host)
|
||||
|
||||
async def async_step_discovery(self, user_input):
|
||||
async def async_step_discovery(self, discovery_info):
|
||||
"""Initialize step from discovery."""
|
||||
_LOGGER.info("Discovered device: %s", user_input)
|
||||
return self._create_entry(user_input[KEY_IP], user_input[KEY_MAC])
|
||||
_LOGGER.debug("Discovered device: %s", discovery_info)
|
||||
await self.async_set_unique_id(discovery_info[KEY_MAC])
|
||||
self._abort_if_unique_id_configured()
|
||||
self.host = discovery_info[KEY_IP]
|
||||
return await self.async_step_user()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Daikin AC",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/daikin",
|
||||
"requirements": ["pydaikin==2.0.1"],
|
||||
"requirements": ["pydaikin==2.0.2"],
|
||||
"codeowners": ["@fredrike"],
|
||||
"quality_scale": "platinum"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Configure Daikin AC",
|
||||
"description": "Enter IP address of your Daikin AC.",
|
||||
"description": "Enter IP address of your Daikin AC.\n\nNote that [%key:common::config_flow::data::api_key%] and [%key:common::config_flow::data::password%] are used by BRP072Cxx and SKYFi devices respectively.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"key": "[%key:common::config_flow::data::api_key%]",
|
||||
|
||||
@@ -158,23 +158,24 @@ class ForkedDaapdFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Prepare configuration for a discovered forked-daapd device."""
|
||||
if not (
|
||||
discovery_info.get("properties")
|
||||
and discovery_info["properties"].get("mtd-version")
|
||||
and int(discovery_info["properties"].get("mtd-version", "0").split(".")[0])
|
||||
>= 27
|
||||
and discovery_info["properties"].get("Machine Name")
|
||||
):
|
||||
return self.async_abort(reason="not_forked_daapd")
|
||||
|
||||
await self.async_set_unique_id(discovery_info["properties"]["Machine Name"])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
# Update title and abort if we already have an entry for this host
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data[CONF_HOST] != discovery_info["host"]:
|
||||
if entry.data.get(CONF_HOST) != discovery_info["host"]:
|
||||
continue
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry, title=discovery_info["properties"]["Machine Name"],
|
||||
)
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
await self.async_set_unique_id(discovery_info["properties"]["Machine Name"])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
zeroconf_data = {
|
||||
CONF_HOST: discovery_info["host"],
|
||||
CONF_PORT: int(discovery_info["port"]),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "frontend",
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"requirements": ["home-assistant-frontend==20200515.0"],
|
||||
"requirements": ["home-assistant-frontend==20200519.0"],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"auth",
|
||||
|
||||
@@ -75,6 +75,7 @@ from .const import (
|
||||
from .util import (
|
||||
convert_to_float,
|
||||
dismiss_setup_message,
|
||||
format_sw_version,
|
||||
show_setup_message,
|
||||
validate_media_player_features,
|
||||
)
|
||||
@@ -253,7 +254,7 @@ class HomeAccessory(Accessory):
|
||||
else:
|
||||
model = domain.title()
|
||||
if ATTR_SOFTWARE_VERSION in self.config:
|
||||
sw_version = self.config[ATTR_SOFTWARE_VERSION]
|
||||
sw_version = format_sw_version(self.config[ATTR_SOFTWARE_VERSION])
|
||||
else:
|
||||
sw_version = __version__
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import io
|
||||
import ipaddress
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
import socket
|
||||
|
||||
@@ -415,6 +416,14 @@ def get_aid_storage_fullpath_for_entry_id(hass: HomeAssistant, entry_id: str):
|
||||
)
|
||||
|
||||
|
||||
def format_sw_version(version):
|
||||
"""Extract the version string in a format homekit can consume."""
|
||||
match = re.search(r"([0-9]+)(\.[0-9]+)?(\.[0-9]+)?", str(version).replace("-", "."))
|
||||
if match:
|
||||
return match.group(0)
|
||||
return None
|
||||
|
||||
|
||||
def migrate_filesystem_state_data_for_primary_imported_entry_id(
|
||||
hass: HomeAssistant, entry_id: str
|
||||
):
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
"name": "Mill",
|
||||
"documentation": "https://www.home-assistant.io/integrations/mill",
|
||||
"requirements": ["millheater==0.3.4"],
|
||||
"codeowners": ["@danielhiversen"]
|
||||
"codeowners": ["@danielhiversen"],
|
||||
"config_flow": true
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import (
|
||||
COORDINATOR,
|
||||
@@ -61,7 +61,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_update_data():
|
||||
"""Fetch data from NUT."""
|
||||
async with async_timeout.timeout(10):
|
||||
return await hass.async_add_executor_job(data.update)
|
||||
await hass.async_add_executor_job(data.update)
|
||||
if not data.status:
|
||||
raise UpdateFailed("Error fetching UPS state")
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
|
||||
@@ -189,6 +189,8 @@ class NUTSensor(Entity):
|
||||
@property
|
||||
def state(self):
|
||||
"""Return entity state from ups."""
|
||||
if not self._data.status:
|
||||
return None
|
||||
if self._type == KEY_STATUS_DISPLAY:
|
||||
return _format_display_state(self._data.status)
|
||||
return self._data.status.get(self._type)
|
||||
|
||||
@@ -54,6 +54,8 @@ class ONVIFDevice:
|
||||
self.profiles: List[Profile] = []
|
||||
self.max_resolution: int = 0
|
||||
|
||||
self._dt_diff_seconds: int = 0
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of this device."""
|
||||
@@ -100,6 +102,16 @@ class ONVIFDevice:
|
||||
if self.capabilities.ptz:
|
||||
self.device.create_ptz_service()
|
||||
|
||||
if self._dt_diff_seconds > 300 and self.capabilities.events:
|
||||
self.capabilities.events = False
|
||||
LOGGER.warning(
|
||||
"The system clock on '%s' is more than 5 minutes off. "
|
||||
"Although this device supports events, they will be "
|
||||
"disabled until the device clock is fixed as we will "
|
||||
"not be able to renew the subscription.",
|
||||
self.name,
|
||||
)
|
||||
|
||||
if self.capabilities.events:
|
||||
self.events = EventManager(
|
||||
self.hass, self.device, self.config_entry.unique_id
|
||||
@@ -179,9 +191,9 @@ class ONVIFDevice:
|
||||
)
|
||||
|
||||
dt_diff = cam_date - system_date
|
||||
dt_diff_seconds = dt_diff.total_seconds()
|
||||
self._dt_diff_seconds = dt_diff.total_seconds()
|
||||
|
||||
if dt_diff_seconds > 5:
|
||||
if self._dt_diff_seconds > 5:
|
||||
LOGGER.warning(
|
||||
"The date/time on the device (UTC) is '%s', "
|
||||
"which is different from the system '%s', "
|
||||
|
||||
@@ -104,7 +104,8 @@ class EventManager:
|
||||
if not self._subscription:
|
||||
return
|
||||
|
||||
await self._subscription.Renew(dt_util.utcnow() + dt.timedelta(minutes=10))
|
||||
termination_time = (dt_util.utcnow() + dt.timedelta(minutes=30)).isoformat()
|
||||
await self._subscription.Renew(termination_time)
|
||||
|
||||
async def async_pull_messages(self, _now: dt = None) -> None:
|
||||
"""Pull messages from device."""
|
||||
|
||||
@@ -308,7 +308,7 @@ async def async_parse_last_reboot(uid: str, msg) -> Event:
|
||||
dt_util.parse_datetime(msg.Message._value_1.Data.SimpleItem[0].Value)
|
||||
),
|
||||
)
|
||||
except (AttributeError, KeyError):
|
||||
except (AttributeError, KeyError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ async def async_parse_last_reset(uid: str, msg) -> Event:
|
||||
),
|
||||
entity_enabled=False,
|
||||
)
|
||||
except (AttributeError, KeyError):
|
||||
except (AttributeError, KeyError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
@@ -354,5 +354,5 @@ async def async_parse_last_clock_sync(uid: str, msg) -> Event:
|
||||
),
|
||||
entity_enabled=False,
|
||||
)
|
||||
except (AttributeError, KeyError):
|
||||
except (AttributeError, KeyError, ValueError):
|
||||
return None
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"codeowners": [
|
||||
"@danielhiversen"
|
||||
],
|
||||
"requirements": ["open-garage==0.1.3"]
|
||||
"requirements": ["open-garage==0.1.4"]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Sonos",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/sonos",
|
||||
"requirements": ["pysonos==0.0.29"],
|
||||
"requirements": ["pysonos==0.0.30"],
|
||||
"ssdp": [
|
||||
{
|
||||
"st": "urn:schemas-upnp-org:device:ZonePlayer:1"
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/zha",
|
||||
"requirements": [
|
||||
"bellows==0.16.1",
|
||||
"bellows==0.16.2",
|
||||
"pyserial==3.4",
|
||||
"zha-quirks==0.0.39",
|
||||
"zigpy-cc==0.4.2",
|
||||
"zigpy-deconz==0.9.2",
|
||||
"zigpy==0.20.3",
|
||||
"zigpy==0.20.4",
|
||||
"zigpy-xbee==0.12.1",
|
||||
"zigpy-zigate==0.6.1"
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 110
|
||||
PATCH_VERSION = "0b3"
|
||||
PATCH_VERSION = "0b5"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER = (3, 7, 0)
|
||||
|
||||
+17
-8
@@ -1454,10 +1454,6 @@ class Config:
|
||||
)
|
||||
data = await store.async_load()
|
||||
|
||||
if data and "external_url" in data:
|
||||
self._update(source=SOURCE_STORAGE, **data)
|
||||
return
|
||||
|
||||
async def migrate_base_url(_: Event) -> None:
|
||||
"""Migrate base_url to internal_url/external_url."""
|
||||
if self.hass.config.api is None:
|
||||
@@ -1484,11 +1480,24 @@ class Config:
|
||||
external_url=network.normalize_url(str(base_url))
|
||||
)
|
||||
|
||||
# Try to migrate base_url to internal_url/external_url
|
||||
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, migrate_base_url)
|
||||
|
||||
if data:
|
||||
self._update(source=SOURCE_STORAGE, **data)
|
||||
# Try to migrate base_url to internal_url/external_url
|
||||
if "external_url" not in data:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, migrate_base_url
|
||||
)
|
||||
|
||||
self._update(
|
||||
source=SOURCE_STORAGE,
|
||||
latitude=data.get("latitude"),
|
||||
longitude=data.get("longitude"),
|
||||
elevation=data.get("elevation"),
|
||||
unit_system=data.get("unit_system"),
|
||||
location_name=data.get("location_name"),
|
||||
time_zone=data.get("time_zone"),
|
||||
external_url=data.get("external_url", _UNDEF),
|
||||
internal_url=data.get("internal_url", _UNDEF),
|
||||
)
|
||||
|
||||
async def async_store(self) -> None:
|
||||
"""Store [homeassistant] core config."""
|
||||
|
||||
@@ -89,6 +89,7 @@ FLOWS = [
|
||||
"met",
|
||||
"meteo_france",
|
||||
"mikrotik",
|
||||
"mill",
|
||||
"minecraft_server",
|
||||
"mobile_app",
|
||||
"monoprice",
|
||||
|
||||
@@ -12,7 +12,7 @@ cryptography==2.9.2
|
||||
defusedxml==0.6.0
|
||||
distro==1.5.0
|
||||
hass-nabucasa==0.34.2
|
||||
home-assistant-frontend==20200515.0
|
||||
home-assistant-frontend==20200519.0
|
||||
importlib-metadata==1.6.0
|
||||
jinja2>=2.11.1
|
||||
netdisco==2.6.0
|
||||
|
||||
@@ -333,7 +333,7 @@ beautifulsoup4==4.9.0
|
||||
beewi_smartclim==0.0.7
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.16.1
|
||||
bellows==0.16.2
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.7.5
|
||||
@@ -731,7 +731,7 @@ hole==0.5.1
|
||||
holidays==0.10.2
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20200515.0
|
||||
home-assistant-frontend==20200519.0
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
@@ -997,7 +997,7 @@ onkyo-eiscp==1.2.7
|
||||
onvif-zeep-async==0.3.0
|
||||
|
||||
# homeassistant.components.opengarage
|
||||
open-garage==0.1.3
|
||||
open-garage==0.1.4
|
||||
|
||||
# homeassistant.components.opencv
|
||||
# opencv-python-headless==4.2.0.32
|
||||
@@ -1212,7 +1212,7 @@ pyalmond==0.0.2
|
||||
pyarlo==0.2.3
|
||||
|
||||
# homeassistant.components.atag
|
||||
pyatag==0.3.1.1
|
||||
pyatag==0.3.1.2
|
||||
|
||||
# homeassistant.components.netatmo
|
||||
pyatmo==3.3.1
|
||||
@@ -1263,7 +1263,7 @@ pycsspeechtts==1.0.3
|
||||
# pycups==1.9.73
|
||||
|
||||
# homeassistant.components.daikin
|
||||
pydaikin==2.0.1
|
||||
pydaikin==2.0.2
|
||||
|
||||
# homeassistant.components.danfoss_air
|
||||
pydanfossair==0.1.0
|
||||
@@ -1615,7 +1615,7 @@ pysnmp==4.4.12
|
||||
pysoma==0.0.10
|
||||
|
||||
# homeassistant.components.sonos
|
||||
pysonos==0.0.29
|
||||
pysonos==0.0.30
|
||||
|
||||
# homeassistant.components.spc
|
||||
pyspcwebgw==0.4.0
|
||||
@@ -2263,7 +2263,7 @@ zigpy-xbee==0.12.1
|
||||
zigpy-zigate==0.6.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.20.3
|
||||
zigpy==0.20.4
|
||||
|
||||
# homeassistant.components.zoneminder
|
||||
zm-py==0.4.0
|
||||
|
||||
@@ -147,7 +147,7 @@ axis==25
|
||||
base36==0.1.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.16.1
|
||||
bellows==0.16.2
|
||||
|
||||
# homeassistant.components.blebox
|
||||
blebox_uniapi==1.3.2
|
||||
@@ -312,7 +312,7 @@ hole==0.5.1
|
||||
holidays==0.10.2
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20200515.0
|
||||
home-assistant-frontend==20200519.0
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
@@ -515,7 +515,7 @@ pyalmond==0.0.2
|
||||
pyarlo==0.2.3
|
||||
|
||||
# homeassistant.components.atag
|
||||
pyatag==0.3.1.1
|
||||
pyatag==0.3.1.2
|
||||
|
||||
# homeassistant.components.netatmo
|
||||
pyatmo==3.3.1
|
||||
@@ -533,7 +533,7 @@ pychromecast==5.1.0
|
||||
pycoolmasternet==0.0.4
|
||||
|
||||
# homeassistant.components.daikin
|
||||
pydaikin==2.0.1
|
||||
pydaikin==2.0.2
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==70
|
||||
@@ -681,7 +681,7 @@ pysmartthings==0.7.1
|
||||
pysoma==0.0.10
|
||||
|
||||
# homeassistant.components.sonos
|
||||
pysonos==0.0.29
|
||||
pysonos==0.0.30
|
||||
|
||||
# homeassistant.components.spc
|
||||
pyspcwebgw==0.4.0
|
||||
@@ -918,4 +918,4 @@ zigpy-xbee==0.12.1
|
||||
zigpy-zigate==0.6.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.20.3
|
||||
zigpy==0.20.4
|
||||
|
||||
@@ -6,8 +6,8 @@ from aiohttp import ClientError
|
||||
from aiohttp.web_exceptions import HTTPForbidden
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.daikin import config_flow
|
||||
from homeassistant.components.daikin.const import KEY_IP, KEY_MAC
|
||||
from homeassistant.config_entries import SOURCE_DISCOVERY, SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
@@ -22,13 +22,6 @@ MAC = "AABBCCDDEEFF"
|
||||
HOST = "127.0.0.1"
|
||||
|
||||
|
||||
def init_config_flow(hass):
|
||||
"""Init a configuration flow."""
|
||||
flow = config_flow.FlowHandler()
|
||||
flow.hass = hass
|
||||
return flow
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_daikin():
|
||||
"""Mock pydaikin."""
|
||||
@@ -45,13 +38,16 @@ def mock_daikin():
|
||||
|
||||
async def test_user(hass, mock_daikin):
|
||||
"""Test user config."""
|
||||
flow = init_config_flow(hass)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
result = await flow.async_step_user()
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await flow.async_step_user({CONF_HOST: HOST})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_USER}, data={CONF_HOST: HOST},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == HOST
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
@@ -60,34 +56,26 @@ async def test_user(hass, mock_daikin):
|
||||
|
||||
async def test_abort_if_already_setup(hass, mock_daikin):
|
||||
"""Test we abort if Daikin is already setup."""
|
||||
flow = init_config_flow(hass)
|
||||
MockConfigEntry(domain="daikin", data={KEY_MAC: MAC}).add_to_hass(hass)
|
||||
MockConfigEntry(domain="daikin", unique_id=MAC).add_to_hass(hass)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_USER}, data={CONF_HOST: HOST, KEY_MAC: MAC},
|
||||
)
|
||||
|
||||
result = await flow.async_step_user({CONF_HOST: HOST})
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import(hass, mock_daikin):
|
||||
"""Test import step."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
result = await flow.async_step_import({})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_IMPORT}, data={},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await flow.async_step_import({CONF_HOST: HOST})
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == HOST
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
assert result["data"][KEY_MAC] == MAC
|
||||
|
||||
|
||||
async def test_discovery(hass, mock_daikin):
|
||||
"""Test discovery step."""
|
||||
flow = init_config_flow(hass)
|
||||
|
||||
result = await flow.async_step_discovery({KEY_IP: HOST, KEY_MAC: MAC})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_IMPORT}, data={CONF_HOST: HOST},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == HOST
|
||||
assert result["data"][CONF_HOST] == HOST
|
||||
@@ -105,10 +93,31 @@ async def test_discovery(hass, mock_daikin):
|
||||
)
|
||||
async def test_device_abort(hass, mock_daikin, s_effect, reason):
|
||||
"""Test device abort."""
|
||||
flow = init_config_flow(hass)
|
||||
mock_daikin.factory.side_effect = s_effect
|
||||
|
||||
result = await flow.async_step_user({CONF_HOST: HOST})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": SOURCE_USER}, data={CONF_HOST: HOST, KEY_MAC: MAC},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"base": reason}
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"source, data, unique_id", [(SOURCE_DISCOVERY, {KEY_IP: HOST, KEY_MAC: MAC}, MAC)],
|
||||
)
|
||||
async def test_discovery(hass, mock_daikin, source, data, unique_id):
|
||||
"""Test discovery/zeroconf step."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": source}, data=data,
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
MockConfigEntry(domain="daikin", unique_id=unique_id).add_to_hass(hass)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"daikin", context={"source": source}, data=data,
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_in_progress"
|
||||
|
||||
@@ -103,7 +103,7 @@ async def test_zeroconf_updates_title(hass, config_entry):
|
||||
discovery_info = {
|
||||
"host": "192.168.1.1",
|
||||
"port": 23,
|
||||
"properties": {"mtd-version": 1, "Machine Name": "zeroconf_test"},
|
||||
"properties": {"mtd-version": "27.0", "Machine Name": "zeroconf_test"},
|
||||
}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||
@@ -129,12 +129,35 @@ async def test_config_flow_no_websocket(hass, config_entry):
|
||||
|
||||
async def test_config_flow_zeroconf_invalid(hass):
|
||||
"""Test that an invalid zeroconf entry doesn't work."""
|
||||
# test with no discovery properties
|
||||
discovery_info = {"host": "127.0.0.1", "port": 23}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||
) # doesn't create the entry, tries to show form but gets abort
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "not_forked_daapd"
|
||||
# test with forked-daapd version < 27
|
||||
discovery_info = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 23,
|
||||
"properties": {"mtd-version": "26.3", "Machine Name": "forked-daapd"},
|
||||
}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||
) # doesn't create the entry, tries to show form but gets abort
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "not_forked_daapd"
|
||||
# test with verbose mtd-version from Firefly
|
||||
discovery_info = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 23,
|
||||
"properties": {"mtd-version": "0.2.4.1", "Machine Name": "firefly"},
|
||||
}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info
|
||||
) # doesn't create the entry, tries to show form but gets abort
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "not_forked_daapd"
|
||||
|
||||
|
||||
async def test_config_flow_zeroconf_valid(hass):
|
||||
@@ -143,7 +166,7 @@ async def test_config_flow_zeroconf_valid(hass):
|
||||
"host": "192.168.1.1",
|
||||
"port": 23,
|
||||
"properties": {
|
||||
"mtd-version": 1,
|
||||
"mtd-version": "27.0",
|
||||
"Machine Name": "zeroconf_test",
|
||||
"Machine ID": "5E55EEFF",
|
||||
},
|
||||
|
||||
@@ -28,6 +28,7 @@ from homeassistant.components.homekit.util import (
|
||||
density_to_air_quality,
|
||||
dismiss_setup_message,
|
||||
find_next_available_port,
|
||||
format_sw_version,
|
||||
port_is_available,
|
||||
show_setup_message,
|
||||
temperature_to_homekit,
|
||||
@@ -315,3 +316,12 @@ async def test_port_is_available(hass):
|
||||
assert next_port
|
||||
|
||||
assert await hass.async_add_executor_job(port_is_available, next_port)
|
||||
|
||||
|
||||
async def test_format_sw_version():
|
||||
"""Test format_sw_version method."""
|
||||
assert format_sw_version("soho+3.6.8+soho-release-rt120+10") == "3.6.8"
|
||||
assert format_sw_version("undefined-undefined-1.6.8") == "1.6.8"
|
||||
assert format_sw_version("56.0-76060") == "56.0.76060"
|
||||
assert format_sw_version(3.6) == "3.6"
|
||||
assert format_sw_version("unknown") is None
|
||||
|
||||
+14
-3
@@ -1294,17 +1294,17 @@ async def test_migration_base_url(hass, hass_storage):
|
||||
with patch.object(hass.bus, "async_listen_once") as mock_listen:
|
||||
# Empty config
|
||||
await config.async_load()
|
||||
assert len(mock_listen.mock_calls) == 1
|
||||
assert len(mock_listen.mock_calls) == 0
|
||||
|
||||
# With just a name
|
||||
stored["data"] = {"location_name": "Test Name"}
|
||||
await config.async_load()
|
||||
assert len(mock_listen.mock_calls) == 2
|
||||
assert len(mock_listen.mock_calls) == 1
|
||||
|
||||
# With external url
|
||||
stored["data"]["external_url"] = "https://example.com"
|
||||
await config.async_load()
|
||||
assert len(mock_listen.mock_calls) == 2
|
||||
assert len(mock_listen.mock_calls) == 1
|
||||
|
||||
# Test that the event listener works
|
||||
assert mock_listen.mock_calls[0][1][0] == EVENT_HOMEASSISTANT_START
|
||||
@@ -1319,3 +1319,14 @@ async def test_migration_base_url(hass, hass_storage):
|
||||
hass.config.api = Mock(deprecated_base_url=internal)
|
||||
await mock_listen.mock_calls[0][1][1](None)
|
||||
assert config.internal_url == internal
|
||||
|
||||
|
||||
async def test_additional_data_in_core_config(hass, hass_storage):
|
||||
"""Test that we can handle additional data in core configuration."""
|
||||
config = ha.Config(hass)
|
||||
hass_storage[ha.CORE_STORAGE_KEY] = {
|
||||
"version": 1,
|
||||
"data": {"location_name": "Test Name", "additional_valid_key": "value"},
|
||||
}
|
||||
await config.async_load()
|
||||
assert config.location_name == "Test Name"
|
||||
|
||||
Reference in New Issue
Block a user