From f447adc5b66e37ed4359885d5fcbb2d89fc63298 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 12 Jul 2021 10:09:45 -0500 Subject: [PATCH 01/24] Ignore Sonos Boost devices during discovery (#52845) --- homeassistant/components/sonos/__init__.py | 4 ++++ homeassistant/components/sonos/speaker.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index ec16ec5bd87..040ee321206 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -46,6 +46,7 @@ _LOGGER = logging.getLogger(__name__) CONF_ADVERTISE_ADDR = "advertise_addr" CONF_INTERFACE_ADDR = "interface_addr" +DISCOVERY_IGNORED_MODELS = ["Sonos Boost"] CONFIG_SCHEMA = vol.Schema( @@ -233,6 +234,9 @@ async def async_setup_entry( # noqa: C901 @callback def _async_discovered_player(info): + if info.get("modelName") in DISCOVERY_IGNORED_MODELS: + _LOGGER.debug("Ignoring device: %s", info.get("friendlyName")) + return uid = info.get(ssdp.ATTR_UPNP_UDN) if uid.startswith("uuid:"): uid = uid[5:] diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 14adbc337fb..b1483c0f5d3 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -511,7 +511,7 @@ class SonosSpeaker: await self.async_unsubscribe() if not will_reconnect: - self.hass.data[DATA_SONOS].ssdp_known.remove(self.soco.uid) + self.hass.data[DATA_SONOS].ssdp_known.discard(self.soco.uid) self.async_write_entity_states() async def async_rebooted(self, soco: SoCo) -> None: From edf517681cb350f9a8af93f7cefece5f91f262b4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 12 Jul 2021 06:24:12 -1000 Subject: [PATCH 02/24] Add zeroconf discovery to Sonos (#52655) --- homeassistant/components/sonos/__init__.py | 192 ++++++++++-------- homeassistant/components/sonos/config_flow.py | 48 ++++- homeassistant/components/sonos/const.py | 3 + homeassistant/components/sonos/helpers.py | 19 ++ homeassistant/components/sonos/manifest.json | 3 +- homeassistant/components/sonos/speaker.py | 25 ++- homeassistant/components/sonos/strings.json | 1 + .../components/sonos/translations/en.json | 1 + homeassistant/generated/zeroconf.py | 5 + tests/components/sonos/test_config_flow.py | 92 +++++++++ tests/components/sonos/test_helpers.py | 17 ++ 11 files changed, 315 insertions(+), 91 deletions(-) create mode 100644 tests/components/sonos/test_config_flow.py create mode 100644 tests/components/sonos/test_helpers.py diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 040ee321206..3d810c7e1a3 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -31,6 +31,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send, dispatcher_s from .alarms import SonosAlarms from .const import ( DATA_SONOS, + DATA_SONOS_DISCOVERY_MANAGER, DISCOVERY_INTERVAL, DOMAIN, PLATFORMS, @@ -91,7 +92,7 @@ class SonosData: self.alarms: dict[str, SonosAlarms] = {} self.topology_condition = asyncio.Condition() self.hosts_heartbeat = None - self.ssdp_known: set[str] = set() + self.discovery_known: set[str] = set() self.boot_counts: dict[str, int] = {} @@ -111,9 +112,7 @@ async def async_setup(hass, config): return True -async def async_setup_entry( # noqa: C901 - hass: HomeAssistant, entry: ConfigEntry -) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Sonos from a config entry.""" pysonos.config.EVENTS_MODULE = events_asyncio @@ -123,7 +122,6 @@ async def async_setup_entry( # noqa: C901 data = hass.data[DATA_SONOS] config = hass.data[DOMAIN].get("media_player", {}) hosts = config.get(CONF_HOSTS, []) - discovery_lock = asyncio.Lock() _LOGGER.debug("Reached async_setup_entry, config=%s", config) advertise_addr = config.get(CONF_ADVERTISE_ADDR) @@ -137,153 +135,181 @@ async def async_setup_entry( # noqa: C901 deprecated_address, ) - async def _async_stop_event_listener(event: Event) -> None: + manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = SonosDiscoveryManager( + hass, entry, data, hosts + ) + hass.async_create_task(manager.setup_platforms_and_discovery()) + return True + + +def _create_soco(ip_address: str, source: SoCoCreationSource) -> SoCo | None: + """Create a soco instance and return if successful.""" + try: + soco = pysonos.SoCo(ip_address) + # Ensure that the player is available and UID is cached + _ = soco.uid + _ = soco.volume + return soco + except (OSError, SoCoException) as ex: + _LOGGER.warning( + "Failed to connect to %s player '%s': %s", source.value, ip_address, ex + ) + return None + + +class SonosDiscoveryManager: + """Manage sonos discovery.""" + + def __init__( + self, hass: HomeAssistant, entry: ConfigEntry, data: SonosData, hosts: list[str] + ) -> None: + """Init discovery manager.""" + self.hass = hass + self.entry = entry + self.data = data + self.hosts = hosts + self.discovery_lock = asyncio.Lock() + + async def _async_stop_event_listener(self, event: Event) -> None: await asyncio.gather( - *[speaker.async_unsubscribe() for speaker in data.discovered.values()], + *[speaker.async_unsubscribe() for speaker in self.data.discovered.values()], return_exceptions=True, ) if events_asyncio.event_listener: await events_asyncio.event_listener.async_stop() - def _stop_manual_heartbeat(event: Event) -> None: - if data.hosts_heartbeat: - data.hosts_heartbeat() - data.hosts_heartbeat = None + def _stop_manual_heartbeat(self, event: Event) -> None: + if self.data.hosts_heartbeat: + self.data.hosts_heartbeat() + self.data.hosts_heartbeat = None - def _discovered_player(soco: SoCo) -> None: + def _discovered_player(self, soco: SoCo) -> None: """Handle a (re)discovered player.""" try: speaker_info = soco.get_speaker_info(True) _LOGGER.debug("Adding new speaker: %s", speaker_info) - speaker = SonosSpeaker(hass, soco, speaker_info) - data.discovered[soco.uid] = speaker + speaker = SonosSpeaker(self.hass, soco, speaker_info) + self.data.discovered[soco.uid] = speaker for coordinator, coord_dict in [ - (SonosAlarms, data.alarms), - (SonosFavorites, data.favorites), + (SonosAlarms, self.data.alarms), + (SonosFavorites, self.data.favorites), ]: if soco.household_id not in coord_dict: - new_coordinator = coordinator(hass, soco.household_id) + new_coordinator = coordinator(self.hass, soco.household_id) new_coordinator.setup(soco) coord_dict[soco.household_id] = new_coordinator speaker.setup() except (OSError, SoCoException): _LOGGER.warning("Failed to add SonosSpeaker using %s", soco, exc_info=True) - def _create_soco(ip_address: str, source: SoCoCreationSource) -> SoCo | None: - """Create a soco instance and return if successful.""" - try: - soco = pysonos.SoCo(ip_address) - # Ensure that the player is available and UID is cached - _ = soco.uid - _ = soco.volume - return soco - except (OSError, SoCoException) as ex: - _LOGGER.warning( - "Failed to connect to %s player '%s': %s", source.value, ip_address, ex - ) - return None - - def _manual_hosts(now: datetime.datetime | None = None) -> None: + def _manual_hosts(self, now: datetime.datetime | None = None) -> None: """Players from network configuration.""" - for host in hosts: + for host in self.hosts: ip_addr = socket.gethostbyname(host) known_uid = next( ( uid - for uid, speaker in data.discovered.items() + for uid, speaker in self.data.discovered.items() if speaker.soco.ip_address == ip_addr ), None, ) if known_uid: - dispatcher_send(hass, f"{SONOS_SEEN}-{known_uid}") + dispatcher_send(self.hass, f"{SONOS_SEEN}-{known_uid}") else: soco = _create_soco(ip_addr, SoCoCreationSource.CONFIGURED) if soco and soco.is_visible: - _discovered_player(soco) + self._discovered_player(soco) - data.hosts_heartbeat = hass.helpers.event.call_later( - DISCOVERY_INTERVAL.total_seconds(), _manual_hosts + self.data.hosts_heartbeat = self.hass.helpers.event.call_later( + DISCOVERY_INTERVAL.total_seconds(), self._manual_hosts ) @callback - def _async_signal_update_groups(event): - async_dispatcher_send(hass, SONOS_GROUP_UPDATE) + def _async_signal_update_groups(self, _event): + async_dispatcher_send(self.hass, SONOS_GROUP_UPDATE) - def _discovered_ip(ip_address): + def _discovered_ip(self, ip_address): soco = _create_soco(ip_address, SoCoCreationSource.DISCOVERED) if soco and soco.is_visible: - _discovered_player(soco) + self._discovered_player(soco) - async def _async_create_discovered_player(uid, discovered_ip, boot_seqnum): + async def _async_create_discovered_player(self, uid, discovered_ip, boot_seqnum): """Only create one player at a time.""" - async with discovery_lock: - if uid not in data.discovered: - await hass.async_add_executor_job(_discovered_ip, discovered_ip) + async with self.discovery_lock: + if uid not in self.data.discovered: + await self.hass.async_add_executor_job( + self._discovered_ip, discovered_ip + ) return - if boot_seqnum and boot_seqnum > data.boot_counts[uid]: - data.boot_counts[uid] = boot_seqnum - if soco := await hass.async_add_executor_job( + if boot_seqnum and boot_seqnum > self.data.boot_counts[uid]: + self.data.boot_counts[uid] = boot_seqnum + if soco := await self.hass.async_add_executor_job( _create_soco, discovered_ip, SoCoCreationSource.REBOOTED ): - async_dispatcher_send(hass, f"{SONOS_REBOOTED}-{uid}", soco) + async_dispatcher_send(self.hass, f"{SONOS_REBOOTED}-{uid}", soco) else: - async_dispatcher_send(hass, f"{SONOS_SEEN}-{uid}") + async_dispatcher_send(self.hass, f"{SONOS_SEEN}-{uid}") @callback - def _async_discovered_player(info): - if info.get("modelName") in DISCOVERY_IGNORED_MODELS: - _LOGGER.debug("Ignoring device: %s", info.get("friendlyName")) - return + def _async_ssdp_discovered_player(self, info): + discovered_ip = urlparse(info[ssdp.ATTR_SSDP_LOCATION]).hostname + boot_seqnum = info.get("X-RINCON-BOOTSEQ") uid = info.get(ssdp.ATTR_UPNP_UDN) if uid.startswith("uuid:"): uid = uid[5:] - if boot_seqnum := info.get("X-RINCON-BOOTSEQ"): - boot_seqnum = int(boot_seqnum) - data.boot_counts.setdefault(uid, boot_seqnum) - if uid not in data.ssdp_known: - _LOGGER.debug("New discovery: %s", info) - data.ssdp_known.add(uid) - discovered_ip = urlparse(info[ssdp.ATTR_SSDP_LOCATION]).hostname - asyncio.create_task( - _async_create_discovered_player(uid, discovered_ip, boot_seqnum) + self.async_discovered_player( + info, discovered_ip, uid, boot_seqnum, info.get("modelName") ) - async def setup_platforms_and_discovery(): + @callback + def async_discovered_player(self, info, discovered_ip, uid, boot_seqnum, model): + """Handle discovery via ssdp or zeroconf.""" + if model in DISCOVERY_IGNORED_MODELS: + _LOGGER.debug("Ignoring device: %s", info) + return + if boot_seqnum: + boot_seqnum = int(boot_seqnum) + self.data.boot_counts.setdefault(uid, boot_seqnum) + if uid not in self.data.discovery_known: + _LOGGER.debug("New discovery uid=%s: %s", uid, info) + self.data.discovery_known.add(uid) + asyncio.create_task( + self._async_create_discovered_player(uid, discovered_ip, boot_seqnum) + ) + + async def setup_platforms_and_discovery(self): + """Set up platforms and discovery.""" await asyncio.gather( *[ - hass.config_entries.async_forward_entry_setup(entry, platform) + self.hass.config_entries.async_forward_entry_setup(self.entry, platform) for platform in PLATFORMS ] ) - entry.async_on_unload( - hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_START, _async_signal_update_groups + self.entry.async_on_unload( + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, self._async_signal_update_groups ) ) - entry.async_on_unload( - hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STOP, _async_stop_event_listener + self.entry.async_on_unload( + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, self._async_stop_event_listener ) ) _LOGGER.debug("Adding discovery job") - if hosts: - entry.async_on_unload( - hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STOP, _stop_manual_heartbeat + if self.hosts: + self.entry.async_on_unload( + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, self._stop_manual_heartbeat ) ) - await hass.async_add_executor_job(_manual_hosts) + await self.hass.async_add_executor_job(self._manual_hosts) return - entry.async_on_unload( + self.entry.async_on_unload( ssdp.async_register_callback( - hass, _async_discovered_player, {"st": UPNP_ST} + self.hass, self._async_ssdp_discovered_player, {"st": UPNP_ST} ) ) - - hass.async_create_task(setup_platforms_and_discovery()) - - return True diff --git a/homeassistant/components/sonos/config_flow.py b/homeassistant/components/sonos/config_flow.py index 5037abb79aa..1ba750c24be 100644 --- a/homeassistant/components/sonos/config_flow.py +++ b/homeassistant/components/sonos/config_flow.py @@ -1,10 +1,19 @@ """Config flow for SONOS.""" +import logging + import pysonos +from homeassistant import config_entries +from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_entry_flow +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.config_entry_flow import DiscoveryFlowHandler +from homeassistant.helpers.typing import DiscoveryInfoType -from .const import DOMAIN +from .const import DATA_SONOS_DISCOVERY_MANAGER, DOMAIN +from .helpers import hostname_to_uid + +_LOGGER = logging.getLogger(__name__) async def _async_has_devices(hass: HomeAssistant) -> bool: @@ -13,4 +22,37 @@ async def _async_has_devices(hass: HomeAssistant) -> bool: return bool(result) -config_entry_flow.register_discovery_flow(DOMAIN, "Sonos", _async_has_devices) +class SonosDiscoveryFlowHandler(DiscoveryFlowHandler): + """Sonos discovery flow that callsback zeroconf updates.""" + + def __init__(self) -> None: + """Init discovery flow.""" + super().__init__(DOMAIN, "Sonos", _async_has_devices) + + async def async_step_zeroconf( + self, discovery_info: DiscoveryInfoType + ) -> FlowResult: + """Handle a flow initialized by zeroconf.""" + hostname = discovery_info["hostname"] + if hostname is None or not hostname.startswith("Sonos-"): + return self.async_abort(reason="not_sonos_device") + await self.async_set_unique_id(self._domain, raise_on_progress=False) + host = discovery_info[CONF_HOST] + properties = discovery_info["properties"] + boot_seqnum = properties.get("bootseq") + model = properties.get("model") + uid = hostname_to_uid(hostname) + _LOGGER.debug( + "Calling async_discovered_player for %s with uid=%s and boot_seqnum=%s", + host, + uid, + boot_seqnum, + ) + if discovery_manager := self.hass.data.get(DATA_SONOS_DISCOVERY_MANAGER): + discovery_manager.async_discovered_player( + properties, host, uid, boot_seqnum, model + ) + return await self.async_step_discovery(discovery_info) + + +config_entries.HANDLERS.register(DOMAIN)(SonosDiscoveryFlowHandler) diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index 9072f4cab02..aca4b9b39ae 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -26,6 +26,7 @@ UPNP_ST = "urn:schemas-upnp-org:device:ZonePlayer:1" DOMAIN = "sonos" DATA_SONOS = "sonos_media_player" +DATA_SONOS_DISCOVERY_MANAGER = "sonos_discovery_manager" PLATFORMS = {BINARY_SENSOR_DOMAIN, MP_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN} SONOS_ARTIST = "artists" @@ -154,3 +155,5 @@ SCAN_INTERVAL = datetime.timedelta(seconds=10) DISCOVERY_INTERVAL = datetime.timedelta(seconds=60) SEEN_EXPIRE_TIME = 3.5 * DISCOVERY_INTERVAL SUBSCRIPTION_TIMEOUT = 1200 + +MDNS_SERVICE = "_sonos._tcp.local." diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index ac8cd00d9db..675a3e8e9f2 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -9,6 +9,9 @@ from pysonos.exceptions import SoCoException, SoCoUPnPException from homeassistant.exceptions import HomeAssistantError +UID_PREFIX = "RINCON_" +UID_POSTFIX = "01400" + _LOGGER = logging.getLogger(__name__) @@ -36,3 +39,19 @@ def soco_error(errorcodes: list[str] | None = None) -> Callable: return wrapper return decorator + + +def uid_to_short_hostname(uid: str) -> str: + """Convert a Sonos uid to a short hostname.""" + hostname_uid = uid + if hostname_uid.startswith(UID_PREFIX): + hostname_uid = hostname_uid[len(UID_PREFIX) :] + if hostname_uid.endswith(UID_POSTFIX): + hostname_uid = hostname_uid[: -len(UID_POSTFIX)] + return f"Sonos-{hostname_uid}" + + +def hostname_to_uid(hostname: str) -> str: + """Convert a Sonos hostname to a uid.""" + baseuid = hostname.split("-")[1].replace(".local.", "") + return f"{UID_PREFIX}{baseuid}{UID_POSTFIX}" diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index a3b031ac07b..b1b0bc8a202 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -5,7 +5,8 @@ "documentation": "https://www.home-assistant.io/integrations/sonos", "requirements": ["pysonos==0.0.51"], "dependencies": ["ssdp"], - "after_dependencies": ["plex"], + "after_dependencies": ["plex", "zeroconf"], + "zeroconf": ["_sonos._tcp.local."], "ssdp": [ { "st": "urn:schemas-upnp-org:device:ZonePlayer:1" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index b1483c0f5d3..19f65f963c3 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -19,6 +19,7 @@ from pysonos.music_library import MusicLibrary from pysonos.plugins.sharelink import ShareLinkPlugin from pysonos.snapshot import Snapshot +from homeassistant.components import zeroconf from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN @@ -37,6 +38,7 @@ from .const import ( BATTERY_SCAN_INTERVAL, DATA_SONOS, DOMAIN, + MDNS_SERVICE, PLATFORMS, SCAN_INTERVAL, SEEN_EXPIRE_TIME, @@ -56,7 +58,7 @@ from .const import ( SUBSCRIPTION_TIMEOUT, ) from .favorites import SonosFavorites -from .helpers import soco_error +from .helpers import soco_error, uid_to_short_hostname EVENT_CHARGING = { "CHARGING": True, @@ -498,12 +500,27 @@ class SonosSpeaker: self, now: datetime.datetime | None = None, will_reconnect: bool = False ) -> None: """Make this player unavailable when it was not seen recently.""" - self._share_link_plugin = None - if self._seen_timer: self._seen_timer() self._seen_timer = None + hostname = uid_to_short_hostname(self.soco.uid) + zcname = f"{hostname}.{MDNS_SERVICE}" + aiozeroconf = await zeroconf.async_get_async_instance(self.hass) + if await aiozeroconf.async_get_service_info(MDNS_SERVICE, zcname): + # We can still see the speaker via zeroconf check again later. + self._seen_timer = self.hass.helpers.event.async_call_later( + SEEN_EXPIRE_TIME.total_seconds(), self.async_unseen + ) + return + + _LOGGER.debug( + "No activity and could not locate %s on the network. Marking unavailable", + zcname, + ) + + self._share_link_plugin = None + if self._poll_timer: self._poll_timer() self._poll_timer = None @@ -511,7 +528,7 @@ class SonosSpeaker: await self.async_unsubscribe() if not will_reconnect: - self.hass.data[DATA_SONOS].ssdp_known.discard(self.soco.uid) + self.hass.data[DATA_SONOS].discovery_known.discard(self.soco.uid) self.async_write_entity_states() async def async_rebooted(self, soco: SoCo) -> None: diff --git a/homeassistant/components/sonos/strings.json b/homeassistant/components/sonos/strings.json index 12812d66692..fb73e30421f 100644 --- a/homeassistant/components/sonos/strings.json +++ b/homeassistant/components/sonos/strings.json @@ -6,6 +6,7 @@ } }, "abort": { + "not_sonos_device": "Discovered device is not a Sonos device", "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", "no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]" } diff --git a/homeassistant/components/sonos/translations/en.json b/homeassistant/components/sonos/translations/en.json index 38aecd5e965..181ddc2f5bf 100644 --- a/homeassistant/components/sonos/translations/en.json +++ b/homeassistant/components/sonos/translations/en.json @@ -2,6 +2,7 @@ "config": { "abort": { "no_devices_found": "No devices found on the network", + "not_sonos_device": "Discovered device is not a Sonos device", "single_instance_allowed": "Already configured. Only a single configuration possible." }, "step": { diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index 11fd47469f8..536485f7f55 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -186,6 +186,11 @@ ZEROCONF = { "name": "brother*" } ], + "_sonos._tcp.local.": [ + { + "domain": "sonos" + } + ], "_spotify-connect._tcp.local.": [ { "domain": "spotify" diff --git a/tests/components/sonos/test_config_flow.py b/tests/components/sonos/test_config_flow.py new file mode 100644 index 00000000000..9dd308ae28f --- /dev/null +++ b/tests/components/sonos/test_config_flow.py @@ -0,0 +1,92 @@ +"""Test the sonos config flow.""" +from __future__ import annotations + +from unittest.mock import MagicMock, patch + +from homeassistant import config_entries, core, setup +from homeassistant.components.sonos.const import DATA_SONOS_DISCOVERY_MANAGER, DOMAIN + + +@patch("homeassistant.components.sonos.config_flow.pysonos.discover", return_value=True) +async def test_user_form(discover_mock: MagicMock, hass: core.HomeAssistant): + """Test we get the user initiated form.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] is None + with patch( + "homeassistant.components.sonos.async_setup", + return_value=True, + ) as mock_setup, patch( + "homeassistant.components.sonos.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "Sonos" + assert result2["data"] == {} + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_zeroconf_form(hass: core.HomeAssistant): + """Test we pass sonos devices to the discovery manager.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock() + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data={ + "host": "192.168.4.2", + "hostname": "Sonos-aaa", + "properties": {"bootseq": "1234"}, + }, + ) + assert result["type"] == "form" + assert result["errors"] is None + + with patch( + "homeassistant.components.sonos.async_setup", + return_value=True, + ) as mock_setup, patch( + "homeassistant.components.sonos.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "Sonos" + assert result2["data"] == {} + + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + assert len(mock_manager.mock_calls) == 2 + + +async def test_zeroconf_form_not_sonos(hass: core.HomeAssistant): + """Test we abort on non-sonos devices.""" + mock_manager = hass.data[DATA_SONOS_DISCOVERY_MANAGER] = MagicMock() + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data={ + "host": "192.168.4.2", + "hostname": "not-aaa", + "properties": {"bootseq": "1234"}, + }, + ) + assert result["type"] == "abort" + assert result["reason"] == "not_sonos_device" + assert len(mock_manager.mock_calls) == 0 diff --git a/tests/components/sonos/test_helpers.py b/tests/components/sonos/test_helpers.py new file mode 100644 index 00000000000..858657e01c0 --- /dev/null +++ b/tests/components/sonos/test_helpers.py @@ -0,0 +1,17 @@ +"""Test the sonos config flow.""" +from __future__ import annotations + +from homeassistant.components.sonos.helpers import ( + hostname_to_uid, + uid_to_short_hostname, +) + + +async def test_uid_to_short_hostname(): + """Test we can convert a uid to a short hostname.""" + assert uid_to_short_hostname("RINCON_347E5C0CF1E301400") == "Sonos-347E5C0CF1E3" + + +async def test_uid_to_hostname(): + """Test we can convert a hostname to a uid.""" + assert hostname_to_uid("Sonos-347E5C0CF1E3.local.") == "RINCON_347E5C0CF1E301400" From 757388cc319ae36cc4744bce9128cf1df20e0215 Mon Sep 17 00:00:00 2001 From: apaperclip <67401560+apaperclip@users.noreply.github.com> Date: Thu, 8 Jul 2021 12:10:05 -0400 Subject: [PATCH 03/24] Remove scale calculation for climacell cloud cover (#52752) --- homeassistant/components/climacell/const.py | 1 - homeassistant/components/climacell/weather.py | 2 -- tests/components/climacell/test_sensor.py | 2 +- tests/components/climacell/test_weather.py | 4 ++-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/climacell/const.py b/homeassistant/components/climacell/const.py index 062de93375b..057cef5e993 100644 --- a/homeassistant/components/climacell/const.py +++ b/homeassistant/components/climacell/const.py @@ -214,7 +214,6 @@ CC_SENSOR_TYPES = [ ATTR_FIELD: CC_ATTR_CLOUD_COVER, ATTR_NAME: "Cloud Cover", CONF_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_SCALE: 1 / 100, }, { ATTR_FIELD: CC_ATTR_WIND_GUST, diff --git a/homeassistant/components/climacell/weather.py b/homeassistant/components/climacell/weather.py index 1b3b50e8566..be03b53ef72 100644 --- a/homeassistant/components/climacell/weather.py +++ b/homeassistant/components/climacell/weather.py @@ -207,8 +207,6 @@ class BaseClimaCellWeatherEntity(ClimaCellEntity, WeatherEntity): distance_convert(self.wind_gust, LENGTH_MILES, LENGTH_KILOMETERS), 4 ) cloud_cover = self.cloud_cover - if cloud_cover is not None: - cloud_cover /= 100 return { ATTR_CLOUD_COVER: cloud_cover, ATTR_WIND_GUST: wind_gust, diff --git a/tests/components/climacell/test_sensor.py b/tests/components/climacell/test_sensor.py index d93bdb5fae8..c642457b63e 100644 --- a/tests/components/climacell/test_sensor.py +++ b/tests/components/climacell/test_sensor.py @@ -182,7 +182,7 @@ async def test_v4_sensor( check_sensor_state(hass, PRESSURE_SURFACE_LEVEL, "997.9688") check_sensor_state(hass, GHI, "0.0") check_sensor_state(hass, CLOUD_BASE, "1.1909") - check_sensor_state(hass, CLOUD_COVER, "1.0") + check_sensor_state(hass, CLOUD_COVER, "100") check_sensor_state(hass, CLOUD_CEILING, "1.1909") check_sensor_state(hass, WIND_GUST, "5.6506") check_sensor_state(hass, PRECIPITATION_TYPE, "rain") diff --git a/tests/components/climacell/test_weather.py b/tests/components/climacell/test_weather.py index fa1ef9dc490..90efdea3c8c 100644 --- a/tests/components/climacell/test_weather.py +++ b/tests/components/climacell/test_weather.py @@ -228,7 +228,7 @@ async def test_v3_weather( assert weather_state.attributes[ATTR_WEATHER_VISIBILITY] == 9.9940 assert weather_state.attributes[ATTR_WEATHER_WIND_BEARING] == 320.31 assert weather_state.attributes[ATTR_WEATHER_WIND_SPEED] == 14.6289 - assert weather_state.attributes[ATTR_CLOUD_COVER] == 1 + assert weather_state.attributes[ATTR_CLOUD_COVER] == 100 assert weather_state.attributes[ATTR_WIND_GUST] == 24.0758 assert weather_state.attributes[ATTR_PRECIPITATION_TYPE] == "rain" @@ -391,6 +391,6 @@ async def test_v4_weather( assert weather_state.attributes[ATTR_WEATHER_VISIBILITY] == 13.1162 assert weather_state.attributes[ATTR_WEATHER_WIND_BEARING] == 315.14 assert weather_state.attributes[ATTR_WEATHER_WIND_SPEED] == 15.0152 - assert weather_state.attributes[ATTR_CLOUD_COVER] == 1 + assert weather_state.attributes[ATTR_CLOUD_COVER] == 100 assert weather_state.attributes[ATTR_WIND_GUST] == 20.3421 assert weather_state.attributes[ATTR_PRECIPITATION_TYPE] == "rain" From f4fa0d7789bc28173a617adc05236a1636cd532a Mon Sep 17 00:00:00 2001 From: Jc2k Date: Thu, 8 Jul 2021 17:26:25 +0100 Subject: [PATCH 04/24] Fix homebridge devices becoming unavailable frequently (#52753) Update to aiohomekit 0.4.3 and make sure service type UUID is normalised before comparison Co-authored-by: J. Nick Koston --- .../components/homekit_controller/manifest.json | 2 +- homeassistant/components/homekit_controller/sensor.py | 3 ++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/homekit_controller/test_sensor.py | 10 ++++++++++ 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 496d629d112..2d40cc8a235 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==0.4.2"], + "requirements": ["aiohomekit==0.4.3"], "zeroconf": ["_hap._tcp.local."], "after_dependencies": ["zeroconf"], "codeowners": ["@Jc2k", "@bdraco"], diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index fe98b75130c..b21010e9b1e 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -44,7 +44,8 @@ SIMPLE_SENSOR = { "unit": TEMP_CELSIUS, # This sensor is only for temperature characteristics that are not part # of a temperature sensor service. - "probe": lambda char: char.service.type != ServicesTypes.TEMPERATURE_SENSOR, + "probe": lambda char: char.service.type + != ServicesTypes.get_uuid(ServicesTypes.TEMPERATURE_SENSOR), }, } diff --git a/requirements_all.txt b/requirements_all.txt index 9d2ad7b154b..2fbaa378fbc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -175,7 +175,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.4.2 +aiohomekit==0.4.3 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3bad09bcaf5..47e667e5bfe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -112,7 +112,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.4.2 +aiohomekit==0.4.3 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/tests/components/homekit_controller/test_sensor.py b/tests/components/homekit_controller/test_sensor.py index a79e94c4bb7..604c83e54f7 100644 --- a/tests/components/homekit_controller/test_sensor.py +++ b/tests/components/homekit_controller/test_sensor.py @@ -86,6 +86,16 @@ async def test_temperature_sensor_read_state(hass, utcnow): assert state.attributes["device_class"] == DEVICE_CLASS_TEMPERATURE +async def test_temperature_sensor_not_added_twice(hass, utcnow): + """A standalone temperature sensor should not get a characteristic AND a service entity.""" + helper = await setup_test_component( + hass, create_temperature_sensor_service, suffix="temperature" + ) + + for state in hass.states.async_all(): + assert state.entity_id == helper.entity_id + + async def test_humidity_sensor_read_state(hass, utcnow): """Test reading the state of a HomeKit humidity sensor accessory.""" helper = await setup_test_component( From 8af63cd9a00c88a3b590b3d93cdb5960ff89580c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 12 Jul 2021 09:39:51 -1000 Subject: [PATCH 05/24] Fix nexia thermostats humidify without dehumidify support (#52758) --- .coveragerc | 1 + homeassistant/components/nexia/climate.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index e05d97b66fb..06f6bca0eec 100644 --- a/.coveragerc +++ b/.coveragerc @@ -687,6 +687,7 @@ omit = homeassistant/components/netgear_lte/* homeassistant/components/netio/switch.py homeassistant/components/neurio_energy/sensor.py + homeassistant/components/nexia/climate.py homeassistant/components/nextcloud/* homeassistant/components/nfandroidtv/notify.py homeassistant/components/niko_home_control/light.py diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 2dff498f281..e27a1816a8e 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -203,7 +203,10 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): def set_humidity(self, humidity): """Dehumidify target.""" - self._thermostat.set_dehumidify_setpoint(humidity / 100.0) + if self._thermostat.has_dehumidify_support(): + self._thermostat.set_dehumidify_setpoint(humidity / 100.0) + else: + self._thermostat.set_humidify_setpoint(humidity / 100.0) self._signal_thermostat_update() @property From 763f8ac6a8fcc4163e2a216580af679050dcfbe7 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Fri, 9 Jul 2021 10:51:46 +0100 Subject: [PATCH 06/24] Support certain homekit devices that emit invalid JSON (#52759) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 2d40cc8a235..816ec2db4d9 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==0.4.3"], + "requirements": ["aiohomekit==0.5.0"], "zeroconf": ["_hap._tcp.local."], "after_dependencies": ["zeroconf"], "codeowners": ["@Jc2k", "@bdraco"], diff --git a/requirements_all.txt b/requirements_all.txt index 2fbaa378fbc..df8db3bf1b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -175,7 +175,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.4.3 +aiohomekit==0.5.0 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 47e667e5bfe..c04a82524b2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -112,7 +112,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.4.3 +aiohomekit==0.5.0 # homeassistant.components.emulated_hue # homeassistant.components.http From 87d4544531edf2151860c59d474d85dcadb084a8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Jul 2021 11:03:48 -1000 Subject: [PATCH 07/24] Send ssdp requests to ipv4 broadcast as well (#52760) * Send ssdp requests to 255.255.255.255 as well - This matches pysonos behavior and may fix reports of inability to discover some sonos devices https://github.com/amelchio/pysonos/blob/master/pysonos/discovery.py#L120 * Update homeassistant/components/ssdp/__init__.py --- homeassistant/components/ssdp/__init__.py | 17 ++++++++++++- tests/components/ssdp/test_init.py | 31 +++++++++++++---------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index d03f8967311..9896ec4177e 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -29,6 +29,8 @@ from .flow import FlowDispatcher, SSDPFlow DOMAIN = "ssdp" SCAN_INTERVAL = timedelta(seconds=60) +IPV4_BROADCAST = IPv4Address("255.255.255.255") + # Attributes for accessing info from SSDP response ATTR_SSDP_LOCATION = "ssdp_location" ATTR_SSDP_ST = "ssdp_st" @@ -236,7 +238,20 @@ class Scanner: async_callback=self._async_process_entry, source_ip=source_ip ) ) - + try: + IPv4Address(source_ip) + except ValueError: + continue + # Some sonos devices only seem to respond if we send to the broadcast + # address. This matches pysonos' behavior + # https://github.com/amelchio/pysonos/blob/d4329b4abb657d106394ae69357805269708c996/pysonos/discovery.py#L120 + self._ssdp_listeners.append( + SSDPListener( + async_callback=self._async_process_entry, + source_ip=source_ip, + target_ip=IPV4_BROADCAST, + ) + ) self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.async_stop) self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STARTED, self.flow_dispatcher.async_start diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index 6c019f1f311..568a2261fee 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -295,15 +295,15 @@ async def test_start_stop_scanner(async_start_mock, async_search_mock, hass): await hass.async_block_till_done() async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200)) await hass.async_block_till_done() - assert async_start_mock.call_count == 1 - assert async_search_mock.call_count == 1 + assert async_start_mock.call_count == 2 + assert async_search_mock.call_count == 2 hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200)) await hass.async_block_till_done() - assert async_start_mock.call_count == 1 - assert async_search_mock.call_count == 1 + assert async_start_mock.call_count == 2 + assert async_search_mock.call_count == 2 async def test_unexpected_exception_while_fetching(hass, aioclient_mock, caplog): @@ -459,11 +459,11 @@ async def test_scan_with_registered_callback(hass, aioclient_mock, caplog): await hass.async_block_till_done() assert hass.state == CoreState.running - assert len(integration_callbacks) == 3 - assert len(integration_callbacks_from_cache) == 3 - assert len(integration_match_all_callbacks) == 3 + assert len(integration_callbacks) == 5 + assert len(integration_callbacks_from_cache) == 5 + assert len(integration_match_all_callbacks) == 5 assert len(integration_match_all_not_present_callbacks) == 0 - assert len(match_any_callbacks) == 3 + assert len(match_any_callbacks) == 5 assert len(not_matching_integration_callbacks) == 0 assert integration_callbacks[0] == { ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", @@ -546,7 +546,7 @@ async def test_unsolicited_ssdp_registered_callback(hass, aioclient_mock, caplog assert hass.state == CoreState.running assert ( - len(integration_callbacks) == 2 + len(integration_callbacks) == 4 ) # unsolicited callbacks without st are not cached assert integration_callbacks[0] == { "UDN": "uuid:RINCON_1111BB963FD801400", @@ -635,7 +635,7 @@ async def test_scan_second_hit(hass, aioclient_mock, caplog): async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200)) await hass.async_block_till_done() - assert len(integration_callbacks) == 2 + assert len(integration_callbacks) == 4 assert integration_callbacks[0] == { ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_SSDP_EXT: "", @@ -781,7 +781,12 @@ async def test_async_detect_interfaces_setting_empty_route(hass): hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() - assert {create_args[0][1]["source_ip"], create_args[1][1]["source_ip"]} == { - IPv4Address("192.168.1.5"), - IPv6Address("2001:db8::"), + argset = set() + for argmap in create_args: + argset.add((argmap[1].get("source_ip"), argmap[1].get("target_ip"))) + + assert argset == { + (IPv6Address("2001:db8::"), None), + (IPv4Address("192.168.1.5"), IPv4Address("255.255.255.255")), + (IPv4Address("192.168.1.5"), None), } From ce20ca7bb683838439b9a071dfb036e2513ee96a Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 9 Jul 2021 11:54:40 +0200 Subject: [PATCH 08/24] Bump dependency to properly handle current and voltage not being reported on some zhapower endpoints (#52764) --- homeassistant/components/deconz/manifest.json | 10 +++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index ad57b1bd903..fbd420e4b16 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -3,13 +3,17 @@ "name": "deCONZ", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/deconz", - "requirements": ["pydeconz==80"], + "requirements": [ + "pydeconz==81" + ], "ssdp": [ { "manufacturer": "Royal Philips Electronics" } ], - "codeowners": ["@Kane610"], + "codeowners": [ + "@Kane610" + ], "quality_scale": "platinum", "iot_class": "local_push" -} +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index df8db3bf1b2..344c93ddecb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1375,7 +1375,7 @@ pydaikin==2.4.4 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==80 +pydeconz==81 # homeassistant.components.delijn pydelijn==0.6.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c04a82524b2..678d3b63fd7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -770,7 +770,7 @@ pycoolmasternet-async==0.1.2 pydaikin==2.4.4 # homeassistant.components.deconz -pydeconz==80 +pydeconz==81 # homeassistant.components.dexcom pydexcom==0.2.0 From df3c8586b5e3ec2c112bfdaf00e13799d8014137 Mon Sep 17 00:00:00 2001 From: Brandon Rothweiler Date: Fri, 9 Jul 2021 01:55:26 -0400 Subject: [PATCH 09/24] Upgrade pymazda to 0.2.0 (#52775) --- homeassistant/components/mazda/__init__.py | 6 ++++-- homeassistant/components/mazda/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/mazda/test_init.py | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mazda/__init__.py b/homeassistant/components/mazda/__init__.py index d704cfb7f44..921dd4c06c5 100644 --- a/homeassistant/components/mazda/__init__.py +++ b/homeassistant/components/mazda/__init__.py @@ -50,7 +50,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: region = entry.data[CONF_REGION] websession = aiohttp_client.async_get_clientsession(hass) - mazda_client = MazdaAPI(email, password, region, websession) + mazda_client = MazdaAPI( + email, password, region, websession=websession, use_cached_vehicle_list=True + ) try: await mazda_client.validate_credentials() @@ -166,7 +168,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER, name=DOMAIN, update_method=async_update_data, - update_interval=timedelta(seconds=60), + update_interval=timedelta(seconds=180), ) hass.data.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/mazda/manifest.json b/homeassistant/components/mazda/manifest.json index dd169159bc8..cc12653f5cb 100644 --- a/homeassistant/components/mazda/manifest.json +++ b/homeassistant/components/mazda/manifest.json @@ -3,7 +3,7 @@ "name": "Mazda Connected Services", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/mazda", - "requirements": ["pymazda==0.1.6"], + "requirements": ["pymazda==0.2.0"], "codeowners": ["@bdr99"], "quality_scale": "platinum", "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index 344c93ddecb..d968079f526 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1571,7 +1571,7 @@ pymailgunner==1.4 pymata-express==1.19 # homeassistant.components.mazda -pymazda==0.1.6 +pymazda==0.2.0 # homeassistant.components.mediaroom pymediaroom==0.6.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 678d3b63fd7..a2ecb78e251 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -888,7 +888,7 @@ pymailgunner==1.4 pymata-express==1.19 # homeassistant.components.mazda -pymazda==0.1.6 +pymazda==0.2.0 # homeassistant.components.melcloud pymelcloud==2.5.3 diff --git a/tests/components/mazda/test_init.py b/tests/components/mazda/test_init.py index 0c47ae8f2e0..8b135f15e80 100644 --- a/tests/components/mazda/test_init.py +++ b/tests/components/mazda/test_init.py @@ -97,7 +97,7 @@ async def test_update_auth_failure(hass: HomeAssistant): "homeassistant.components.mazda.MazdaAPI.get_vehicles", side_effect=MazdaAuthenticationException("Login failed"), ): - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=61)) + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=181)) await hass.async_block_till_done() flows = hass.config_entries.flow.async_progress() @@ -136,7 +136,7 @@ async def test_update_general_failure(hass: HomeAssistant): "homeassistant.components.mazda.MazdaAPI.get_vehicles", side_effect=Exception("Unknown exception"), ): - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=61)) + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=181)) await hass.async_block_till_done() entity = hass.states.get("sensor.my_mazda3_fuel_remaining_percentage") From f7c65e7c8ba120d7af21da9e0e04d8417d7d6d69 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 9 Jul 2021 11:38:38 +0200 Subject: [PATCH 10/24] Fix ESPHome Camera not merging image packets (#52783) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index e48cd4847c8..d8a22534001 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==5.0.0"], + "requirements": ["aioesphomeapi==5.0.1"], "zeroconf": ["_esphomelib._tcp.local."], "codeowners": ["@OttoWinter", "@jesserockz"], "after_dependencies": ["zeroconf", "tag"], diff --git a/requirements_all.txt b/requirements_all.txt index d968079f526..4f1f3bc4523 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -160,7 +160,7 @@ aioeafm==0.1.2 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==5.0.0 +aioesphomeapi==5.0.1 # homeassistant.components.flo aioflo==0.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a2ecb78e251..d1583ce3ddb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -100,7 +100,7 @@ aioeafm==0.1.2 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==5.0.0 +aioesphomeapi==5.0.1 # homeassistant.components.flo aioflo==0.4.1 From 473f109428acc70e7d6d489298c27658a102cde5 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Fri, 9 Jul 2021 19:12:51 +0200 Subject: [PATCH 11/24] Fix Neato parameter for token refresh (#52785) * Fix param * cleanup --- homeassistant/components/neato/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/neato/__init__.py b/homeassistant/components/neato/__init__.py index f61db94332b..28569e0f1d7 100644 --- a/homeassistant/components/neato/__init__.py +++ b/homeassistant/components/neato/__init__.py @@ -77,9 +77,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) - session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) - - neato_session = api.ConfigEntryAuth(hass, entry, session) + neato_session = api.ConfigEntryAuth(hass, entry, implementation) hass.data[NEATO_DOMAIN][entry.entry_id] = neato_session hub = NeatoHub(hass, Account(neato_session)) From 798f3eada28e061c410783717bb88a6957a1efbf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 12 Jul 2021 04:30:54 -1000 Subject: [PATCH 12/24] Add the Trane brand to nexia (#52805) --- homeassistant/components/nexia/config_flow.py | 20 +++++++++++++++---- homeassistant/components/nexia/const.py | 3 ++- homeassistant/components/nexia/manifest.json | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/nexia/config_flow.py b/homeassistant/components/nexia/config_flow.py index 18c20a8f92a..4e48123a5de 100644 --- a/homeassistant/components/nexia/config_flow.py +++ b/homeassistant/components/nexia/config_flow.py @@ -1,7 +1,7 @@ """Config flow for Nexia integration.""" import logging -from nexia.const import BRAND_ASAIR, BRAND_NEXIA +from nexia.const import BRAND_ASAIR, BRAND_NEXIA, BRAND_TRANE from nexia.home import NexiaHome from requests.exceptions import ConnectTimeout, HTTPError import voluptuous as vol @@ -9,7 +9,13 @@ import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from .const import BRAND_ASAIR_NAME, BRAND_NEXIA_NAME, CONF_BRAND, DOMAIN +from .const import ( + BRAND_ASAIR_NAME, + BRAND_NEXIA_NAME, + BRAND_TRANE_NAME, + CONF_BRAND, + DOMAIN, +) from .util import is_invalid_auth_code _LOGGER = logging.getLogger(__name__) @@ -19,7 +25,11 @@ DATA_SCHEMA = vol.Schema( vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Required(CONF_BRAND, default=BRAND_NEXIA): vol.In( - {BRAND_NEXIA: BRAND_NEXIA_NAME, BRAND_ASAIR: BRAND_ASAIR_NAME} + { + BRAND_NEXIA: BRAND_NEXIA_NAME, + BRAND_ASAIR: BRAND_ASAIR_NAME, + BRAND_TRANE: BRAND_TRANE_NAME, + } ), } ) @@ -31,7 +41,9 @@ async def validate_input(hass: core.HomeAssistant, data): Data has the keys from DATA_SCHEMA with values provided by the user. """ - state_file = hass.config.path(f"nexia_config_{data[CONF_USERNAME]}.conf") + state_file = hass.config.path( + f"{data[CONF_BRAND]}_config_{data[CONF_USERNAME]}.conf" + ) try: nexia_home = NexiaHome( username=data[CONF_USERNAME], diff --git a/homeassistant/components/nexia/const.py b/homeassistant/components/nexia/const.py index d6e3e5f8008..22b24c3b764 100644 --- a/homeassistant/components/nexia/const.py +++ b/homeassistant/components/nexia/const.py @@ -33,4 +33,5 @@ SIGNAL_ZONE_UPDATE = "NEXIA_CLIMATE_ZONE_UPDATE" SIGNAL_THERMOSTAT_UPDATE = "NEXIA_CLIMATE_THERMOSTAT_UPDATE" BRAND_NEXIA_NAME = "Nexia" -BRAND_ASAIR_NAME = "American Standard" +BRAND_ASAIR_NAME = "American Standard Home" +BRAND_TRANE_NAME = "Trane Home" diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index ed1247ee9e3..eb471597ec6 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", - "name": "Nexia/American Standard", - "requirements": ["nexia==0.9.7"], + "name": "Nexia/American Standard/Trane", + "requirements": ["nexia==0.9.9"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 4f1f3bc4523..b0dc9aba1de 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1020,7 +1020,7 @@ nettigo-air-monitor==1.0.0 neurio==0.3.1 # homeassistant.components.nexia -nexia==0.9.7 +nexia==0.9.9 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d1583ce3ddb..1640fedfb3f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -574,7 +574,7 @@ netdisco==2.9.0 nettigo-air-monitor==1.0.0 # homeassistant.components.nexia -nexia==0.9.7 +nexia==0.9.9 # homeassistant.components.notify_events notify-events==1.0.4 From 320ca4012420d55887e2bdae18d61718aaf8b15b Mon Sep 17 00:00:00 2001 From: Ron Klinkien Date: Mon, 12 Jul 2021 20:21:10 +0200 Subject: [PATCH 13/24] Bump python-fireservicerota to 0.0.42 (#52807) --- homeassistant/components/fireservicerota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/fireservicerota/manifest.json b/homeassistant/components/fireservicerota/manifest.json index 0e2259b6b5e..f35be9e839f 100644 --- a/homeassistant/components/fireservicerota/manifest.json +++ b/homeassistant/components/fireservicerota/manifest.json @@ -3,7 +3,7 @@ "name": "FireServiceRota", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/fireservicerota", - "requirements": ["pyfireservicerota==0.0.40"], + "requirements": ["pyfireservicerota==0.0.42"], "codeowners": ["@cyberjunky"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index b0dc9aba1de..e4f09e78560 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1423,7 +1423,7 @@ pyezviz==0.1.8.9 pyfido==2.1.1 # homeassistant.components.fireservicerota -pyfireservicerota==0.0.40 +pyfireservicerota==0.0.42 # homeassistant.components.flexit pyflexit==0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1640fedfb3f..ee0253a6b24 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -791,7 +791,7 @@ pyezviz==0.1.8.9 pyfido==2.1.1 # homeassistant.components.fireservicerota -pyfireservicerota==0.0.40 +pyfireservicerota==0.0.42 # homeassistant.components.flume pyflume==0.5.5 From 4e82659f3adf0ab78e465e94e85457eb9fd8a581 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Fri, 9 Jul 2021 22:37:56 -0400 Subject: [PATCH 14/24] Bump up ZHA depdencies (#52818) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index d37abea2310..081941d94fe 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -10,7 +10,7 @@ "zha-quirks==0.0.59", "zigpy-cc==0.5.2", "zigpy-deconz==0.12.0", - "zigpy==0.35.1", + "zigpy==0.35.2", "zigpy-xbee==0.13.0", "zigpy-zigate==0.7.3", "zigpy-znp==0.5.1" diff --git a/requirements_all.txt b/requirements_all.txt index e4f09e78560..97ea28aa9a5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2451,7 +2451,7 @@ zigpy-zigate==0.7.3 zigpy-znp==0.5.1 # homeassistant.components.zha -zigpy==0.35.1 +zigpy==0.35.2 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ee0253a6b24..3287e678551 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1345,7 +1345,7 @@ zigpy-zigate==0.7.3 zigpy-znp==0.5.1 # homeassistant.components.zha -zigpy==0.35.1 +zigpy==0.35.2 # homeassistant.components.zwave_js zwave-js-server-python==0.27.0 From 279c34f6061cac40043ad2a0d75912b2d63afd49 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 10 Jul 2021 22:31:42 +0200 Subject: [PATCH 15/24] Update arcam lib to 0.7.0 (#52829) --- homeassistant/components/arcam_fmj/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/arcam_fmj/manifest.json b/homeassistant/components/arcam_fmj/manifest.json index d38ceceba73..6685ea240eb 100644 --- a/homeassistant/components/arcam_fmj/manifest.json +++ b/homeassistant/components/arcam_fmj/manifest.json @@ -3,7 +3,7 @@ "name": "Arcam FMJ Receivers", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/arcam_fmj", - "requirements": ["arcam-fmj==0.5.3"], + "requirements": ["arcam-fmj==0.7.0"], "ssdp": [ { "deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1", diff --git a/requirements_all.txt b/requirements_all.txt index 97ea28aa9a5..1210c323fab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -290,7 +290,7 @@ aprslib==0.6.46 aqualogic==2.6 # homeassistant.components.arcam_fmj -arcam-fmj==0.5.3 +arcam-fmj==0.7.0 # homeassistant.components.arris_tg2492lg arris-tg2492lg==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3287e678551..d8d21fe294e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -191,7 +191,7 @@ apprise==0.9.3 aprslib==0.6.46 # homeassistant.components.arcam_fmj -arcam-fmj==0.5.3 +arcam-fmj==0.7.0 # homeassistant.components.dlna_dmr # homeassistant.components.ssdp From 725a021c2e3c3bce37ac4ea4283f76d1ad95c57e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 12 Jul 2021 04:13:07 -1000 Subject: [PATCH 16/24] Bump aiohomekit to 0.5.1 to solve performance regression (#52878) - Changelog: https://github.com/Jc2k/aiohomekit/compare/0.5.0...0.5.1 - Note that #52759 will need to be cherry-picked under this commit --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 816ec2db4d9..4bc61f53cc0 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==0.5.0"], + "requirements": ["aiohomekit==0.5.1"], "zeroconf": ["_hap._tcp.local."], "after_dependencies": ["zeroconf"], "codeowners": ["@Jc2k", "@bdraco"], diff --git a/requirements_all.txt b/requirements_all.txt index 1210c323fab..66ca1e7bdfd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -175,7 +175,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.5.0 +aiohomekit==0.5.1 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d8d21fe294e..32982c43b01 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -112,7 +112,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.5.0 +aiohomekit==0.5.1 # homeassistant.components.emulated_hue # homeassistant.components.http From d1eadd28b2ff78b340e24b9ec9759ca6b0488e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sun, 11 Jul 2021 22:33:03 +0200 Subject: [PATCH 17/24] Bump pyhaversion to 21.7.0 (#52880) --- homeassistant/components/version/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/version/manifest.json b/homeassistant/components/version/manifest.json index 6f36c337a76..de43a47d505 100644 --- a/homeassistant/components/version/manifest.json +++ b/homeassistant/components/version/manifest.json @@ -3,7 +3,7 @@ "name": "Version", "documentation": "https://www.home-assistant.io/integrations/version", "requirements": [ - "pyhaversion==21.5.0" + "pyhaversion==21.7.0" ], "codeowners": [ "@fabaff", diff --git a/requirements_all.txt b/requirements_all.txt index 66ca1e7bdfd..f9fa5e0a2a0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1466,7 +1466,7 @@ pygtfs==0.1.6 pygti==0.9.2 # homeassistant.components.version -pyhaversion==21.5.0 +pyhaversion==21.7.0 # homeassistant.components.heos pyheos==0.7.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 32982c43b01..086f91afaff 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -819,7 +819,7 @@ pygatt[GATTTOOL]==4.0.5 pygti==0.9.2 # homeassistant.components.version -pyhaversion==21.5.0 +pyhaversion==21.7.0 # homeassistant.components.heos pyheos==0.7.2 From 2a5b447bfdde131d1bd62ca76ec3dc327e348541 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 12 Jul 2021 08:17:50 +0200 Subject: [PATCH 18/24] Prefer using xy over hs when supported by light (#52883) --- homeassistant/components/deconz/light.py | 8 ++++-- tests/components/deconz/test_light.py | 34 +++++++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 90d5e82af71..058147189e6 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -26,6 +26,7 @@ from homeassistant.components.light import ( ) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.util.color import color_hs_to_xy from .const import ( COVER_TYPES, @@ -189,8 +190,11 @@ class DeconzBaseLight(DeconzDevice, LightEntity): data["ct"] = kwargs[ATTR_COLOR_TEMP] if ATTR_HS_COLOR in kwargs: - data["hue"] = int(kwargs[ATTR_HS_COLOR][0] / 360 * 65535) - data["sat"] = int(kwargs[ATTR_HS_COLOR][1] / 100 * 255) + if COLOR_MODE_XY in self._attr_supported_color_modes: + data["xy"] = color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) + else: + data["hue"] = int(kwargs[ATTR_HS_COLOR][0] / 360 * 65535) + data["sat"] = int(kwargs[ATTR_HS_COLOR][1] / 100 * 255) if ATTR_XY_COLOR in kwargs: data["xy"] = kwargs[ATTR_XY_COLOR] diff --git a/tests/components/deconz/test_light.py b/tests/components/deconz/test_light.py index 2beb339dd4f..42dc04fc7ae 100644 --- a/tests/components/deconz/test_light.py +++ b/tests/components/deconz/test_light.py @@ -371,6 +371,34 @@ async def test_light_state_change(hass, aioclient_mock, mock_deconz_websocket): @pytest.mark.parametrize( "input,expected", [ + ( # Turn on light with hue and sat + { + "light_on": True, + "service": SERVICE_TURN_ON, + "call": { + ATTR_ENTITY_ID: "light.hue_go", + ATTR_HS_COLOR: (20, 30), + }, + }, + { + "on": True, + "xy": (0.411, 0.351), + }, + ), + ( # Turn on light with XY color + { + "light_on": True, + "service": SERVICE_TURN_ON, + "call": { + ATTR_ENTITY_ID: "light.hue_go", + ATTR_XY_COLOR: (0.411, 0.351), + }, + }, + { + "on": True, + "xy": (0.411, 0.351), + }, + ), ( # Turn on light with short color loop { "light_on": False, @@ -811,9 +839,8 @@ async def test_groups(hass, aioclient_mock, input, expected): }, }, { - "hue": 45510, "on": True, - "sat": 127, + "xy": (0.235, 0.164), }, ), ( # Turn on group with short color loop @@ -827,9 +854,8 @@ async def test_groups(hass, aioclient_mock, input, expected): }, }, { - "hue": 45510, "on": True, - "sat": 127, + "xy": (0.235, 0.164), }, ), ], From ed200ee4fd477b05ba403677edf0741413115452 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sun, 11 Jul 2021 16:27:46 -0400 Subject: [PATCH 19/24] Bump zwave-js-server-python to 0.27.1 (#52885) --- homeassistant/components/zwave_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index a48d0513c17..d719e3976a4 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave JS", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["zwave-js-server-python==0.27.0"], + "requirements": ["zwave-js-server-python==0.27.1"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["http", "websocket_api"], "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index f9fa5e0a2a0..d146a539d7b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2457,4 +2457,4 @@ zigpy==0.35.2 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.27.0 +zwave-js-server-python==0.27.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 086f91afaff..789dad4910b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1348,4 +1348,4 @@ zigpy-znp==0.5.1 zigpy==0.35.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.27.0 +zwave-js-server-python==0.27.1 From fd848911a68655b3da34ddbe44d42e3b301940fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Mon, 12 Jul 2021 15:57:26 +0200 Subject: [PATCH 20/24] Surepetcare, fix set_lock_state (#52912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/surepetcare/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/surepetcare/__init__.py b/homeassistant/components/surepetcare/__init__.py index 8f0c2311518..e9a2c5b73a1 100644 --- a/homeassistant/components/surepetcare/__init__.py +++ b/homeassistant/components/surepetcare/__init__.py @@ -158,10 +158,10 @@ class SurePetcareAPI: # https://github.com/PyCQA/pylint/issues/2062 # pylint: disable=no-member if state == LockState.UNLOCKED.name.lower(): - await self.surepy.unlock(flap_id) + await self.surepy.sac.unlock(flap_id) elif state == LockState.LOCKED_IN.name.lower(): - await self.surepy.lock_in(flap_id) + await self.surepy.sac.lock_in(flap_id) elif state == LockState.LOCKED_OUT.name.lower(): - await self.surepy.lock_out(flap_id) + await self.surepy.sac.lock_out(flap_id) elif state == LockState.LOCKED_ALL.name.lower(): - await self.surepy.lock(flap_id) + await self.surepy.sac.lock(flap_id) From 3301783c57ab1aac0bf1c894108ac0f6cdc1d2e4 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Mon, 12 Jul 2021 14:02:56 -0400 Subject: [PATCH 21/24] Bump pyinsteon to 1.0.11 (#52927) --- homeassistant/components/insteon/manifest.json | 10 +++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index dc564ae0d70..353cd55c747 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -2,8 +2,12 @@ "domain": "insteon", "name": "Insteon", "documentation": "https://www.home-assistant.io/integrations/insteon", - "requirements": ["pyinsteon==1.0.9"], - "codeowners": ["@teharris1"], + "requirements": [ + "pyinsteon==1.0.11" + ], + "codeowners": [ + "@teharris1" + ], "config_flow": true, "iot_class": "local_push" -} +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index d146a539d7b..1016c73fc84 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1490,7 +1490,7 @@ pyialarm==1.9.0 pyicloud==0.10.2 # homeassistant.components.insteon -pyinsteon==1.0.9 +pyinsteon==1.0.11 # homeassistant.components.intesishome pyintesishome==1.7.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 789dad4910b..40b8cb00d7d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -837,7 +837,7 @@ pyialarm==1.9.0 pyicloud==0.10.2 # homeassistant.components.insteon -pyinsteon==1.0.9 +pyinsteon==1.0.11 # homeassistant.components.ipma pyipma==2.0.5 From 0b04e0d5da7926d0e789f659832c90833ea4b41e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 12 Jul 2021 10:03:13 -1000 Subject: [PATCH 22/24] Fix recorder purge with sqlite3 < 3.32.0 (#52929) --- homeassistant/components/recorder/const.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 026628a32df..eab3c30e99e 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -7,4 +7,9 @@ DOMAIN = "recorder" CONF_DB_INTEGRITY_CHECK = "db_integrity_check" # The maximum number of rows (events) we purge in one delete statement -MAX_ROWS_TO_PURGE = 1000 + +# sqlite3 has a limit of 999 until version 3.32.0 +# in https://github.com/sqlite/sqlite/commit/efdba1a8b3c6c967e7fae9c1989c40d420ce64cc +# We can increase this back to 1000 once most +# have upgraded their sqlite version +MAX_ROWS_TO_PURGE = 998 From 86589b401bd2bbdd927dd7f03f4c186f695c133f Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 12 Jul 2021 15:50:51 -0500 Subject: [PATCH 23/24] Bump pysonos to 0.0.52 (#52934) --- homeassistant/components/sonos/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index b1b0bc8a202..e873b43839a 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,7 +3,7 @@ "name": "Sonos", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sonos", - "requirements": ["pysonos==0.0.51"], + "requirements": ["pysonos==0.0.52"], "dependencies": ["ssdp"], "after_dependencies": ["plex", "zeroconf"], "zeroconf": ["_sonos._tcp.local."], diff --git a/requirements_all.txt b/requirements_all.txt index 1016c73fc84..8634b6957ea 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1773,7 +1773,7 @@ pysnmp==4.4.12 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.51 +pysonos==0.0.52 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 40b8cb00d7d..b9799999a79 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1006,7 +1006,7 @@ pysmartthings==0.7.6 pysoma==0.0.10 # homeassistant.components.sonos -pysonos==0.0.51 +pysonos==0.0.52 # homeassistant.components.spc pyspcwebgw==0.4.0 From e095b9a1b9977dfed69be363f78d2b758d40f723 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 12 Jul 2021 13:57:47 -0700 Subject: [PATCH 24/24] Bumped version to 2021.7.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 373f382a06e..2c90713249c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ from typing import Final MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 7 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)