Compare commits

...

15 Commits

Author SHA1 Message Date
Franck Nijhof 99634e22bd Bumped version to 2023.8.0b2 2023-07-30 19:18:42 +02:00
J. Nick Koston 4bd4c5666d Revert using has_entity_name in ESPHome when friendly_name is not set (#97488) 2023-07-30 19:18:24 +02:00
Marc Mueller 93c536882b Update ha-av to 10.1.1 (#97481) 2023-07-30 19:18:21 +02:00
starkillerOG 3764c2e9de Reolink long poll recover (#97465) 2023-07-30 19:18:18 +02:00
tronikos b23286ce6f Bump opower to 0.0.16 (#97437) 2023-07-30 19:18:15 +02:00
J. Nick Koston 734c16b816 Bump nexia to 2.0.7 (#97432) 2023-07-30 19:18:12 +02:00
Joost Lekkerkerker f1fc09cb1d Small cleanup in event entity (#97409) 2023-07-30 19:18:09 +02:00
Joost Lekkerkerker 364e7b838a Return the actual media url from media extractor (#97408) 2023-07-30 19:18:05 +02:00
G Johansson 7f9db40390 Manual trigger entity fix name influence entity_id (#97398) 2023-07-30 19:18:02 +02:00
starkillerOG 38e22f5614 Regard long poll without events as valid (#97383) 2023-07-30 19:17:59 +02:00
G Johansson 945959827d Bump pysensibo to 1.0.32 (#97382) 2023-07-30 19:17:56 +02:00
Chris Talkington 1a0593fc9a Allow deleting config entry devices in jellyfin (#97377) 2023-07-30 19:17:53 +02:00
J. Nick Koston f54c36ec16 Bump dbus-fast to 1.87.5 (#97364) 2023-07-30 19:17:50 +02:00
starkillerOG 3beffb5103 Bump reolink_aio to 0.7.5 (#97357)
* bump reolink-aio to 0.7.4

* Bump reolink_aio to 0.7.5
2023-07-30 19:17:47 +02:00
Niels Perfors 78dad22fb3 Upgrade Verisure to 2.6.4 (#97278) 2023-07-30 19:17:42 +02:00
24 changed files with 146 additions and 52 deletions
@@ -19,6 +19,6 @@
"bluetooth-adapters==0.16.0",
"bluetooth-auto-recovery==1.2.1",
"bluetooth-data-tools==1.6.1",
"dbus-fast==1.87.2"
"dbus-fast==1.87.5"
]
}
+1 -1
View File
@@ -140,7 +140,6 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]):
"""Define a base esphome entity."""
_attr_should_poll = False
_attr_has_entity_name = True
_static_info: _InfoT
_state: _StateT
_has_state: bool
@@ -169,6 +168,7 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]):
connections={(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)}
)
self._entry_id = entry_data.entry_id
self._attr_has_entity_name = bool(device_info.friendly_name)
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
+1 -2
View File
@@ -45,7 +45,6 @@ __all__ = [
"EventDeviceClass",
"EventEntity",
"EventEntityDescription",
"EventEntityFeature",
]
# mypy: disallow-any-generics
@@ -104,7 +103,7 @@ class EventExtraStoredData(ExtraStoredData):
class EventEntity(RestoreEntity):
"""Representation of a Event entity."""
"""Representation of an Event entity."""
entity_description: EventEntityDescription
_attr_device_class: EventDeviceClass | None
@@ -6,5 +6,5 @@
"dependencies": ["http"],
"documentation": "https://www.home-assistant.io/integrations/generic",
"iot_class": "local_push",
"requirements": ["ha-av==10.1.0", "Pillow==10.0.0"]
"requirements": ["ha-av==10.1.1", "Pillow==10.0.0"]
}
@@ -4,6 +4,7 @@ from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input
from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, LOGGER, PLATFORMS
@@ -60,3 +61,18 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN].pop(entry.entry_id)
return True
async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry
) -> bool:
"""Remove device from a config entry."""
data = hass.data[DOMAIN][config_entry.entry_id]
coordinator = data.coordinators["sessions"]
return not device_entry.identifiers.intersection(
(
(DOMAIN, coordinator.server_id),
*((DOMAIN, id) for id in coordinator.device_ids),
)
)
@@ -47,6 +47,7 @@ class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC):
self.user_id: str = user_id
self.session_ids: set[str] = set()
self.device_ids: set[str] = set()
async def _async_update_data(self) -> JellyfinDataT:
"""Get the latest data from Jellyfin."""
@@ -75,4 +76,6 @@ class SessionsDataUpdateCoordinator(
and session["Client"] != USER_APP_NAME
}
self.device_ids = {session["DeviceId"] for session in sessions_by_id.values()}
return sessions_by_id
@@ -127,7 +127,12 @@ class MediaExtractor:
_LOGGER.error("Could not extract stream for the query: %s", query)
raise MEQueryException() from err
return requested_stream["webpage_url"]
if "formats" in requested_stream:
best_stream = requested_stream["formats"][
len(requested_stream["formats"]) - 1
]
return best_stream["url"]
return requested_stream["url"]
return stream_selector
+1 -1
View File
@@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/nexia",
"iot_class": "cloud_polling",
"loggers": ["nexia"],
"requirements": ["nexia==2.0.6"]
"requirements": ["nexia==2.0.7"]
}
@@ -6,5 +6,5 @@
"dependencies": ["recorder"],
"documentation": "https://www.home-assistant.io/integrations/opower",
"iot_class": "cloud_polling",
"requirements": ["opower==0.0.15"]
"requirements": ["opower==0.0.16"]
}
+21 -3
View File
@@ -267,7 +267,19 @@ class ReolinkHost:
async def _async_start_long_polling(self):
"""Start ONVIF long polling task."""
if self._long_poll_task is None:
await self._api.subscribe(sub_type=SubType.long_poll)
try:
await self._api.subscribe(sub_type=SubType.long_poll)
except ReolinkError as err:
# make sure the long_poll_task is always created to try again later
if not self._lost_subscription:
self._lost_subscription = True
_LOGGER.error(
"Reolink %s event long polling subscription lost: %s",
self._api.nvr_name,
str(err),
)
else:
self._lost_subscription = False
self._long_poll_task = asyncio.create_task(self._async_long_polling())
async def _async_stop_long_polling(self):
@@ -319,7 +331,13 @@ class ReolinkHost:
try:
await self._renew(SubType.push)
if self._long_poll_task is not None:
await self._renew(SubType.long_poll)
if not self._api.subscribed(SubType.long_poll):
_LOGGER.debug("restarting long polling task")
# To prevent 5 minute request timeout
await self._async_stop_long_polling()
await self._async_start_long_polling()
else:
await self._renew(SubType.long_poll)
except SubscriptionError as err:
if not self._lost_subscription:
self._lost_subscription = True
@@ -457,7 +475,7 @@ class ReolinkHost:
self._long_poll_error = False
if not self._long_poll_received and channels != []:
if not self._long_poll_received:
self._long_poll_received = True
ir.async_delete_issue(self._hass, DOMAIN, "webhook_url")
@@ -18,5 +18,5 @@
"documentation": "https://www.home-assistant.io/integrations/reolink",
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"requirements": ["reolink-aio==0.7.3"]
"requirements": ["reolink-aio==0.7.5"]
}
@@ -157,7 +157,6 @@ class ScrapeSensor(
"""Initialize a web scrape sensor."""
CoordinatorEntity.__init__(self, coordinator)
ManualTriggerEntity.__init__(self, hass, trigger_entity_config)
self._attr_name = trigger_entity_config[CONF_NAME].template
self._attr_native_unit_of_measurement = unit_of_measurement
self._attr_state_class = state_class
self._select = select
@@ -15,5 +15,5 @@
"iot_class": "cloud_polling",
"loggers": ["pysensibo"],
"quality_scale": "platinum",
"requirements": ["pysensibo==1.0.31"]
"requirements": ["pysensibo==1.0.32"]
}
+4 -5
View File
@@ -344,16 +344,15 @@ class SQLSensor(ManualTriggerEntity, SensorEntity):
self._attr_extra_state_attributes = {}
self._use_database_executor = use_database_executor
self._lambda_stmt = _generate_lambda_stmt(query)
self._attr_name = (
None if not yaml else trigger_entity_config[CONF_NAME].template
)
self._attr_has_entity_name = not yaml
if not yaml:
self._attr_name = None
self._attr_has_entity_name = True
if not yaml and trigger_entity_config.get(CONF_UNIQUE_ID):
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, trigger_entity_config[CONF_UNIQUE_ID])},
manufacturer="SQL",
name=trigger_entity_config[CONF_NAME].template,
name=self.name,
)
async def async_added_to_hass(self) -> None:
@@ -7,5 +7,5 @@
"integration_type": "system",
"iot_class": "local_push",
"quality_scale": "internal",
"requirements": ["PyTurboJPEG==1.7.1", "ha-av==10.1.0", "numpy==1.23.2"]
"requirements": ["PyTurboJPEG==1.7.1", "ha-av==10.1.1", "numpy==1.23.2"]
}
@@ -7,7 +7,6 @@ from time import sleep
from verisure import (
Error as VerisureError,
LoginError as VerisureLoginError,
ResponseError as VerisureResponseError,
Session as Verisure,
)
@@ -50,7 +49,7 @@ class VerisureDataUpdateCoordinator(DataUpdateCoordinator):
except VerisureLoginError as ex:
LOGGER.error("Could not log in to verisure, %s", ex)
raise ConfigEntryAuthFailed("Credentials expired for Verisure") from ex
except VerisureResponseError as ex:
except VerisureError as ex:
LOGGER.error("Could not log in to verisure, %s", ex)
return False
@@ -65,11 +64,9 @@ class VerisureDataUpdateCoordinator(DataUpdateCoordinator):
try:
await self.hass.async_add_executor_job(self.verisure.update_cookie)
except VerisureLoginError as ex:
LOGGER.error("Credentials expired for Verisure, %s", ex)
raise ConfigEntryAuthFailed("Credentials expired for Verisure") from ex
except VerisureResponseError as ex:
LOGGER.error("Could not log in to verisure, %s", ex)
raise ConfigEntryAuthFailed("Could not log in to verisure") from ex
except VerisureError as ex:
raise UpdateFailed("Unable to update cookie") from ex
try:
overview = await self.hass.async_add_executor_job(
self.verisure.request,
@@ -81,13 +78,6 @@ class VerisureDataUpdateCoordinator(DataUpdateCoordinator):
self.verisure.smart_lock(),
self.verisure.smartplugs(),
)
except VerisureResponseError as err:
LOGGER.debug("Cookie expired or service unavailable, %s", err)
overview = self._overview
try:
await self.hass.async_add_executor_job(self.verisure.update_cookie)
except VerisureResponseError as ex:
raise ConfigEntryAuthFailed("Credentials for Verisure expired.") from ex
except VerisureError as err:
LOGGER.error("Could not read overview, %s", err)
raise UpdateFailed("Could not read overview") from err
@@ -12,5 +12,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["verisure"],
"requirements": ["vsure==2.6.1"]
"requirements": ["vsure==2.6.4"]
}
+1 -1
View File
@@ -7,7 +7,7 @@ from typing import Final
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2023
MINOR_VERSION: Final = 8
PATCH_VERSION: Final = "0b1"
PATCH_VERSION: Final = "0b2"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
+5
View File
@@ -626,6 +626,11 @@ class ManualTriggerEntity(TriggerBaseEntity):
) -> None:
"""Initialize the entity."""
TriggerBaseEntity.__init__(self, hass, config)
# Need initial rendering on `name` as it influence the `entity_id`
self._rendered[CONF_NAME] = config[CONF_NAME].async_render(
{},
parse_result=CONF_NAME in self._parse_result,
)
@callback
def _process_manual_data(self, value: Any | None = None) -> None:
+2 -2
View File
@@ -16,9 +16,9 @@ bluetooth-data-tools==1.6.1
certifi>=2021.5.30
ciso8601==2.3.0
cryptography==41.0.2
dbus-fast==1.87.2
dbus-fast==1.87.5
fnv-hash-fast==0.4.0
ha-av==10.1.0
ha-av==10.1.1
hass-nabucasa==0.69.0
hassil==1.2.5
home-assistant-bluetooth==1.10.2
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2023.8.0b1"
version = "2023.8.0b2"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"
+7 -7
View File
@@ -632,7 +632,7 @@ datadog==0.15.0
datapoint==0.9.8
# homeassistant.components.bluetooth
dbus-fast==1.87.2
dbus-fast==1.87.5
# homeassistant.components.debugpy
debugpy==1.6.7
@@ -937,7 +937,7 @@ h2==4.1.0
# homeassistant.components.generic
# homeassistant.components.stream
ha-av==10.1.0
ha-av==10.1.1
# homeassistant.components.ffmpeg
ha-ffmpeg==3.1.0
@@ -1258,7 +1258,7 @@ nettigo-air-monitor==2.1.0
neurio==0.3.1
# homeassistant.components.nexia
nexia==2.0.6
nexia==2.0.7
# homeassistant.components.nextcloud
nextcloudmonitor==1.4.0
@@ -1368,7 +1368,7 @@ openwrt-luci-rpc==1.1.16
openwrt-ubus-rpc==0.0.2
# homeassistant.components.opower
opower==0.0.15
opower==0.0.16
# homeassistant.components.oralb
oralb-ble==0.17.6
@@ -1982,7 +1982,7 @@ pysabnzbd==1.1.1
pysaj==0.0.16
# homeassistant.components.sensibo
pysensibo==1.0.31
pysensibo==1.0.32
# homeassistant.components.serial
# homeassistant.components.zha
@@ -2278,7 +2278,7 @@ renault-api==0.1.13
renson-endura-delta==1.5.0
# homeassistant.components.reolink
reolink-aio==0.7.3
reolink-aio==0.7.5
# homeassistant.components.idteck_prox
rfk101py==0.0.1
@@ -2634,7 +2634,7 @@ volkszaehler==0.4.0
volvooncall==0.10.3
# homeassistant.components.verisure
vsure==2.6.1
vsure==2.6.4
# homeassistant.components.vasttrafik
vtjp==0.1.14
+7 -7
View File
@@ -515,7 +515,7 @@ datadog==0.15.0
datapoint==0.9.8
# homeassistant.components.bluetooth
dbus-fast==1.87.2
dbus-fast==1.87.5
# homeassistant.components.debugpy
debugpy==1.6.7
@@ -735,7 +735,7 @@ h2==4.1.0
# homeassistant.components.generic
# homeassistant.components.stream
ha-av==10.1.0
ha-av==10.1.1
# homeassistant.components.ffmpeg
ha-ffmpeg==3.1.0
@@ -966,7 +966,7 @@ netmap==0.7.0.2
nettigo-air-monitor==2.1.0
# homeassistant.components.nexia
nexia==2.0.6
nexia==2.0.7
# homeassistant.components.nextcloud
nextcloudmonitor==1.4.0
@@ -1037,7 +1037,7 @@ openerz-api==0.2.0
openhomedevice==2.2.0
# homeassistant.components.opower
opower==0.0.15
opower==0.0.16
# homeassistant.components.oralb
oralb-ble==0.17.6
@@ -1474,7 +1474,7 @@ pyrympro==0.0.7
pysabnzbd==1.1.1
# homeassistant.components.sensibo
pysensibo==1.0.31
pysensibo==1.0.32
# homeassistant.components.serial
# homeassistant.components.zha
@@ -1674,7 +1674,7 @@ renault-api==0.1.13
renson-endura-delta==1.5.0
# homeassistant.components.reolink
reolink-aio==0.7.3
reolink-aio==0.7.5
# homeassistant.components.rflink
rflink==0.0.65
@@ -1934,7 +1934,7 @@ voip-utils==0.1.0
volvooncall==0.10.3
# homeassistant.components.verisure
vsure==2.6.1
vsure==2.6.4
# homeassistant.components.vulcan
vulcan-api==2.3.0
+60
View File
@@ -4,10 +4,29 @@ from unittest.mock import MagicMock
from homeassistant.components.jellyfin.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component
from . import async_load_json_fixture
from tests.common import MockConfigEntry
from tests.typing import MockHAClientWebSocket, WebSocketGenerator
async def remove_device(
ws_client: MockHAClientWebSocket, device_id: str, config_entry_id: str
) -> bool:
"""Remove config entry from a device."""
await ws_client.send_json(
{
"id": 1,
"type": "config/device_registry/remove_config_entry",
"config_entry_id": config_entry_id,
"device_id": device_id,
}
)
response = await ws_client.receive_json()
return response["success"]
async def test_config_entry_not_ready(
@@ -66,3 +85,44 @@ async def test_load_unload_config_entry(
await hass.async_block_till_done()
assert mock_config_entry.entry_id not in hass.data[DOMAIN]
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
async def test_device_remove_devices(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
mock_config_entry: MockConfigEntry,
mock_jellyfin: MagicMock,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test we can only remove a device that no longer exists."""
assert await async_setup_component(hass, "config", {})
mock_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
device_entry = device_registry.async_get_device(
identifiers={
(
DOMAIN,
"DEVICE-UUID",
)
},
)
assert (
await remove_device(
await hass_ws_client(hass), device_entry.id, mock_config_entry.entry_id
)
is False
)
old_device_entry = device_registry.async_get_or_create(
config_entry_id=mock_config_entry.entry_id,
identifiers={(DOMAIN, "OLD-DEVICE-UUID")},
)
assert (
await remove_device(
await hass_ws_client(hass), old_device_entry.id, mock_config_entry.entry_id
)
is True
)