mirror of
https://github.com/home-assistant/core.git
synced 2025-09-24 12:29:31 +00:00
Compare commits
93 Commits
openai-mod
...
2025.7.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
777b3128bb | ||
![]() |
d384bee576 | ||
![]() |
f0cb5d5480 | ||
![]() |
725799c73e | ||
![]() |
dc6d2e3e84 | ||
![]() |
4a7d06a68a | ||
![]() |
cd800da357 | ||
![]() |
4c8ab8eb64 | ||
![]() |
60f4d29d60 | ||
![]() |
68b7d09476 | ||
![]() |
c3eb6dea11 | ||
![]() |
f428ffde87 | ||
![]() |
fa207860a0 | ||
![]() |
959c3a8a99 | ||
![]() |
254ccca4e5 | ||
![]() |
5b08724d81 | ||
![]() |
456f992b7e | ||
![]() |
0675e34c62 | ||
![]() |
190c98f5a8 | ||
![]() |
c6bb26be89 | ||
![]() |
d57c5ffa8f | ||
![]() |
68889e1790 | ||
![]() |
8fdc50a29f | ||
![]() |
5656b4c20d | ||
![]() |
b6edcc9422 | ||
![]() |
7a3eb53453 | ||
![]() |
11a2c73e8a | ||
![]() |
1644484c92 | ||
![]() |
8e0a89dc2f | ||
![]() |
9e4b8df344 | ||
![]() |
69fdc1d269 | ||
![]() |
56e0aa103d | ||
![]() |
caf0492009 | ||
![]() |
c6d0aad3d3 | ||
![]() |
1f59b735c6 | ||
![]() |
87af9fc8ba | ||
![]() |
691a0ca065 | ||
![]() |
80384b89a5 | ||
![]() |
f7672985ed | ||
![]() |
d4374dbcc7 | ||
![]() |
c4ddcd64c8 | ||
![]() |
c802430066 | ||
![]() |
649fbfc729 | ||
![]() |
80c52ad8ea | ||
![]() |
150d4716fa | ||
![]() |
dc2736580f | ||
![]() |
f1272ef513 | ||
![]() |
3c2fa023b4 | ||
![]() |
5cf5be8c9c | ||
![]() |
63b21fda1a | ||
![]() |
d87379d083 | ||
![]() |
0990cef917 | ||
![]() |
962ad99c20 | ||
![]() |
9c9836defd | ||
![]() |
e951fc401c | ||
![]() |
00e2a177a5 | ||
![]() |
b6d316c8f2 | ||
![]() |
b8425de0d0 | ||
![]() |
d51a44acbc | ||
![]() |
435465e569 | ||
![]() |
3b047859f9 | ||
![]() |
91cdf1a367 | ||
![]() |
2377b136f3 | ||
![]() |
186c4e7038 | ||
![]() |
d303a7d17e | ||
![]() |
14f059c766 | ||
![]() |
4a10370932 | ||
![]() |
672ffa5984 | ||
![]() |
3d3f2527cb | ||
![]() |
5c3b279f95 | ||
![]() |
59bcf1167a | ||
![]() |
b4d789f8e2 | ||
![]() |
f4ca56052b | ||
![]() |
74f9549431 | ||
![]() |
9650727515 | ||
![]() |
c965da6559 | ||
![]() |
9077965214 | ||
![]() |
2b7992e849 | ||
![]() |
5d6b02f470 | ||
![]() |
a274961593 | ||
![]() |
4e163c4591 | ||
![]() |
3ffec2a655 | ||
![]() |
c646658643 | ||
![]() |
342b4c3442 | ||
![]() |
eb58c10e5e | ||
![]() |
f42e7d982f | ||
![]() |
898ef43750 | ||
![]() |
f806e6ba49 | ||
![]() |
c23bfb1b39 | ||
![]() |
a2ffe32b02 | ||
![]() |
0f32b6331d | ||
![]() |
9a4959560e | ||
![]() |
41ab7b346c |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -37,7 +37,7 @@ on:
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
CACHE_VERSION: 3
|
||||
CACHE_VERSION: 4
|
||||
UV_CACHE_VERSION: 1
|
||||
MYPY_CACHE_VERSION: 1
|
||||
HA_SHORT_VERSION: "2025.7"
|
||||
|
@@ -505,8 +505,13 @@ class ClimateCapabilities(AlexaEntity):
|
||||
):
|
||||
yield AlexaThermostatController(self.hass, self.entity)
|
||||
yield AlexaTemperatureSensor(self.hass, self.entity)
|
||||
if self.entity.domain == water_heater.DOMAIN and (
|
||||
supported_features & water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
||||
if (
|
||||
self.entity.domain == water_heater.DOMAIN
|
||||
and (
|
||||
supported_features
|
||||
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
||||
)
|
||||
and self.entity.attributes.get(water_heater.ATTR_OPERATION_LIST)
|
||||
):
|
||||
yield AlexaModeController(
|
||||
self.entity,
|
||||
@@ -634,7 +639,9 @@ class FanCapabilities(AlexaEntity):
|
||||
self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}"
|
||||
)
|
||||
force_range_controller = False
|
||||
if supported & fan.FanEntityFeature.PRESET_MODE:
|
||||
if supported & fan.FanEntityFeature.PRESET_MODE and self.entity.attributes.get(
|
||||
fan.ATTR_PRESET_MODES
|
||||
):
|
||||
yield AlexaModeController(
|
||||
self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_PRESET_MODE}"
|
||||
)
|
||||
@@ -672,7 +679,11 @@ class RemoteCapabilities(AlexaEntity):
|
||||
yield AlexaPowerController(self.entity)
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
activities = self.entity.attributes.get(remote.ATTR_ACTIVITY_LIST) or []
|
||||
if activities and supported & remote.RemoteEntityFeature.ACTIVITY:
|
||||
if (
|
||||
activities
|
||||
and (supported & remote.RemoteEntityFeature.ACTIVITY)
|
||||
and self.entity.attributes.get(remote.ATTR_ACTIVITY_LIST)
|
||||
):
|
||||
yield AlexaModeController(
|
||||
self.entity, instance=f"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}"
|
||||
)
|
||||
@@ -692,7 +703,9 @@ class HumidifierCapabilities(AlexaEntity):
|
||||
"""Yield the supported interfaces."""
|
||||
yield AlexaPowerController(self.entity)
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & humidifier.HumidifierEntityFeature.MODES:
|
||||
if (
|
||||
supported & humidifier.HumidifierEntityFeature.MODES
|
||||
) and self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES):
|
||||
yield AlexaModeController(
|
||||
self.entity, instance=f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}"
|
||||
)
|
||||
|
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioamazondevices"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["aioamazondevices==3.2.2"]
|
||||
"requirements": ["aioamazondevices==3.5.1"]
|
||||
}
|
||||
|
@@ -13,6 +13,6 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["acme", "hass_nabucasa", "snitun"],
|
||||
"requirements": ["hass-nabucasa==0.104.0"],
|
||||
"requirements": ["hass-nabucasa==0.106.0"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["async_upnp_client"],
|
||||
"requirements": ["async-upnp-client==0.44.0", "getmac==0.9.5"],
|
||||
"requirements": ["async-upnp-client==0.45.0", "getmac==0.9.5"],
|
||||
"ssdp": [
|
||||
{
|
||||
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
|
||||
|
@@ -7,7 +7,7 @@
|
||||
"dependencies": ["ssdp"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["async-upnp-client==0.44.0"],
|
||||
"requirements": ["async-upnp-client==0.45.0"],
|
||||
"ssdp": [
|
||||
{
|
||||
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",
|
||||
|
@@ -63,6 +63,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) ->
|
||||
coordinator = entry.runtime_data
|
||||
coordinator.async_cancel_token_refresh()
|
||||
coordinator.async_cancel_firmware_refresh()
|
||||
coordinator.async_cancel_mac_verification()
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pyenphase"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pyenphase==2.1.0"],
|
||||
"requirements": ["pyenphase==2.2.2"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_enphase-envoy._tcp.local."
|
||||
|
@@ -171,14 +171,19 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
|
||||
|
||||
for device in new_data.devices.values():
|
||||
# create device registry entry for new main devices
|
||||
if (
|
||||
device.ain not in self.data.devices
|
||||
and device.device_and_unit_id[1] is None
|
||||
if device.ain not in self.data.devices and (
|
||||
device.device_and_unit_id[1] is None
|
||||
or (
|
||||
# workaround for sub units without a main device, e.g. Energy 250
|
||||
# https://github.com/home-assistant/core/issues/145204
|
||||
device.device_and_unit_id[1] == "1"
|
||||
and device.device_and_unit_id[0] not in new_data.devices
|
||||
)
|
||||
):
|
||||
dr.async_get(self.hass).async_get_or_create(
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
name=device.name,
|
||||
identifiers={(DOMAIN, device.ain)},
|
||||
identifiers={(DOMAIN, device.device_and_unit_id[0])},
|
||||
manufacturer=device.manufacturer,
|
||||
model=device.productname,
|
||||
sw_version=device.fw_version,
|
||||
|
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20250702.0"]
|
||||
"requirements": ["home-assistant-frontend==20250702.3"]
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ from homeassistant.components.climate import (
|
||||
FAN_HIGH,
|
||||
FAN_LOW,
|
||||
FAN_MEDIUM,
|
||||
FAN_OFF,
|
||||
SWING_BOTH,
|
||||
SWING_HORIZONTAL,
|
||||
SWING_OFF,
|
||||
@@ -31,6 +32,7 @@ from .coordinator import FGLairConfigEntry, FGLairCoordinator
|
||||
from .entity import FGLairEntity
|
||||
|
||||
HA_TO_FUJI_FAN = {
|
||||
FAN_OFF: FanSpeed.QUIET,
|
||||
FAN_LOW: FanSpeed.LOW,
|
||||
FAN_MEDIUM: FanSpeed.MEDIUM,
|
||||
FAN_HIGH: FanSpeed.HIGH,
|
||||
|
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["dacite", "gios"],
|
||||
"requirements": ["gios==6.0.0"]
|
||||
"requirements": ["gios==6.1.2"]
|
||||
}
|
||||
|
@@ -306,6 +306,11 @@ class WebRTCProvider(CameraWebRTCProvider):
|
||||
await self.teardown()
|
||||
raise HomeAssistantError("Camera has no stream source")
|
||||
|
||||
if camera.platform.platform_name == "generic":
|
||||
# This is a workaround to use ffmpeg for generic cameras
|
||||
# A proper fix will be added in the future together with supporting multiple streams per camera
|
||||
stream_source = "ffmpeg:" + stream_source
|
||||
|
||||
if not self.async_is_supported(stream_source):
|
||||
await self.teardown()
|
||||
raise HomeAssistantError("Stream source is not supported by go2rtc")
|
||||
|
@@ -127,7 +127,7 @@ class GoogleCloudSpeechToTextEntity(SpeechToTextEntity):
|
||||
try:
|
||||
responses = await self._client.streaming_recognize(
|
||||
requests=request_generator(),
|
||||
timeout=10,
|
||||
timeout=30,
|
||||
retry=AsyncRetry(initial=0.1, maximum=2.0, multiplier=2.0),
|
||||
)
|
||||
|
||||
|
@@ -218,7 +218,7 @@ class BaseGoogleCloudProvider:
|
||||
|
||||
response = await self._client.synthesize_speech(
|
||||
request,
|
||||
timeout=10,
|
||||
timeout=30,
|
||||
retry=AsyncRetry(initial=0.1, maximum=2.0, multiplier=2.0),
|
||||
)
|
||||
|
||||
|
@@ -7,5 +7,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["habiticalib"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["habiticalib==0.4.0"]
|
||||
"requirements": ["habiticalib==0.4.1"]
|
||||
}
|
||||
|
@@ -1,3 +1 @@
|
||||
"""The hddtemp component."""
|
||||
|
||||
DOMAIN = "hddtemp"
|
||||
|
@@ -22,14 +22,11 @@ from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, create_issue
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_DEVICE = "device"
|
||||
@@ -59,21 +56,6 @@ def setup_platform(
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the HDDTemp sensor."""
|
||||
create_issue(
|
||||
hass,
|
||||
HOMEASSISTANT_DOMAIN,
|
||||
f"deprecated_system_packages_yaml_integration_{DOMAIN}",
|
||||
breaks_in_ha_version="2025.12.0",
|
||||
is_fixable=False,
|
||||
issue_domain=DOMAIN,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_system_packages_yaml_integration",
|
||||
translation_placeholders={
|
||||
"domain": DOMAIN,
|
||||
"integration_title": "hddtemp",
|
||||
},
|
||||
)
|
||||
|
||||
name = config.get(CONF_NAME)
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
|
@@ -41,7 +41,12 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, issue_registry as ir
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import API_DEFAULT_RETRY_AFTER, APPLIANCES_WITH_PROGRAMS, DOMAIN
|
||||
from .const import (
|
||||
API_DEFAULT_RETRY_AFTER,
|
||||
APPLIANCES_WITH_PROGRAMS,
|
||||
BSH_OPERATION_STATE_PAUSE,
|
||||
DOMAIN,
|
||||
)
|
||||
from .utils import get_dict_from_home_connect_error
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -66,6 +71,7 @@ class HomeConnectApplianceData:
|
||||
|
||||
def update(self, other: HomeConnectApplianceData) -> None:
|
||||
"""Update data with data from other instance."""
|
||||
self.commands.clear()
|
||||
self.commands.update(other.commands)
|
||||
self.events.update(other.events)
|
||||
self.info.connected = other.info.connected
|
||||
@@ -201,6 +207,28 @@ class HomeConnectCoordinator(
|
||||
raw_key=status_key.value,
|
||||
value=event.value,
|
||||
)
|
||||
if (
|
||||
status_key == StatusKey.BSH_COMMON_OPERATION_STATE
|
||||
and event.value == BSH_OPERATION_STATE_PAUSE
|
||||
and CommandKey.BSH_COMMON_RESUME_PROGRAM
|
||||
not in (
|
||||
commands := self.data[
|
||||
event_message_ha_id
|
||||
].commands
|
||||
)
|
||||
):
|
||||
# All the appliances that can be paused
|
||||
# should have the resume command available.
|
||||
commands.add(CommandKey.BSH_COMMON_RESUME_PROGRAM)
|
||||
for (
|
||||
listener,
|
||||
context,
|
||||
) in self._special_listeners.values():
|
||||
if (
|
||||
EventKey.BSH_COMMON_APPLIANCE_DEPAIRED
|
||||
not in context
|
||||
):
|
||||
listener()
|
||||
self._call_event_listener(event_message)
|
||||
|
||||
case EventType.NOTIFY:
|
||||
@@ -627,10 +655,7 @@ class HomeConnectCoordinator(
|
||||
"times": str(MAX_EXECUTIONS),
|
||||
"time_window": str(MAX_EXECUTIONS_TIME_WINDOW // 60),
|
||||
"home_connect_resource_url": "https://www.home-connect.com/global/help-support/error-codes#/Togglebox=15362315-13320636-1/",
|
||||
"home_assistant_core_new_issue_url": (
|
||||
"https://github.com/home-assistant/core/issues/new?template=bug_report.yml"
|
||||
f"&integration_name={DOMAIN}&integration_link=https://www.home-assistant.io/integrations/{DOMAIN}/"
|
||||
),
|
||||
"home_assistant_core_issue_url": "https://github.com/home-assistant/core/issues/147299",
|
||||
},
|
||||
)
|
||||
return True
|
||||
|
@@ -130,7 +130,7 @@
|
||||
"step": {
|
||||
"confirm": {
|
||||
"title": "[%key:component::home_connect::issues::home_connect_too_many_connected_paired_events::title%]",
|
||||
"description": "The appliance \"{appliance_name}\" has been reported as connected or paired {times} times in less than {time_window} minutes, so refreshes on connected or paired events has been disabled to avoid exceeding the API rate limit.\n\nPlease refer to the [Home Connect Wi-Fi requirements and recommendations]({home_connect_resource_url}). If everything seems right with your network configuration, restart the appliance.\n\nClick \"submit\" to re-enable the updates.\nIf the issue persists, please create an issue in the [Home Assistant core repository]({home_assistant_core_new_issue_url})."
|
||||
"description": "The appliance \"{appliance_name}\" has been reported as connected or paired {times} times in less than {time_window} minutes, so refreshes on connected or paired events has been disabled to avoid exceeding the API rate limit.\n\nPlease refer to the [Home Connect Wi-Fi requirements and recommendations]({home_connect_resource_url}). If everything seems right with your network configuration, restart the appliance.\n\nClick \"submit\" to re-enable the updates.\nIf the issue persists, please see the following issue in the [Home Assistant core repository]({home_assistant_core_issue_url})."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -113,9 +113,7 @@ class HomematicipHAP:
|
||||
|
||||
self._ws_close_requested = False
|
||||
self._ws_connection_closed = asyncio.Event()
|
||||
self._retry_task: asyncio.Task | None = None
|
||||
self._tries = 0
|
||||
self._accesspoint_connected = True
|
||||
self._get_state_task: asyncio.Task | None = None
|
||||
self.hmip_device_by_entity_id: dict[str, Any] = {}
|
||||
self.reset_connection_listener: Callable | None = None
|
||||
|
||||
@@ -161,17 +159,8 @@ class HomematicipHAP:
|
||||
"""
|
||||
if not self.home.connected:
|
||||
_LOGGER.error("HMIP access point has lost connection with the cloud")
|
||||
self._accesspoint_connected = False
|
||||
self._ws_connection_closed.set()
|
||||
self.set_all_to_unavailable()
|
||||
elif not self._accesspoint_connected:
|
||||
# Now the HOME_CHANGED event has fired indicating the access
|
||||
# point has reconnected to the cloud again.
|
||||
# Explicitly getting an update as entity states might have
|
||||
# changed during access point disconnect."""
|
||||
|
||||
job = self.hass.async_create_task(self.get_state())
|
||||
job.add_done_callback(self.get_state_finished)
|
||||
self._accesspoint_connected = True
|
||||
|
||||
@callback
|
||||
def async_create_entity(self, *args, **kwargs) -> None:
|
||||
@@ -185,20 +174,43 @@ class HomematicipHAP:
|
||||
await asyncio.sleep(30)
|
||||
await self.hass.config_entries.async_reload(self.config_entry.entry_id)
|
||||
|
||||
async def _try_get_state(self) -> None:
|
||||
"""Call get_state in a loop until no error occurs, using exponential backoff on error."""
|
||||
|
||||
# Wait until WebSocket connection is established.
|
||||
while not self.home.websocket_is_connected():
|
||||
await asyncio.sleep(2)
|
||||
|
||||
delay = 8
|
||||
max_delay = 1500
|
||||
while True:
|
||||
try:
|
||||
await self.get_state()
|
||||
break
|
||||
except HmipConnectionError as err:
|
||||
_LOGGER.warning(
|
||||
"Get_state failed, retrying in %s seconds: %s", delay, err
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
delay = min(delay * 2, max_delay)
|
||||
|
||||
async def get_state(self) -> None:
|
||||
"""Update HMIP state and tell Home Assistant."""
|
||||
await self.home.get_current_state_async()
|
||||
self.update_all()
|
||||
|
||||
def get_state_finished(self, future) -> None:
|
||||
"""Execute when get_state coroutine has finished."""
|
||||
"""Execute when try_get_state coroutine has finished."""
|
||||
try:
|
||||
future.result()
|
||||
except HmipConnectionError:
|
||||
# Somehow connection could not recover. Will disconnect and
|
||||
# so reconnect loop is taking over.
|
||||
_LOGGER.error("Updating state after HMIP access point reconnect failed")
|
||||
self.hass.async_create_task(self.home.disable_events())
|
||||
except Exception as err: # noqa: BLE001
|
||||
_LOGGER.error(
|
||||
"Error updating state after HMIP access point reconnect: %s", err
|
||||
)
|
||||
else:
|
||||
_LOGGER.info(
|
||||
"Updating state after HMIP access point reconnect finished successfully",
|
||||
)
|
||||
|
||||
def set_all_to_unavailable(self) -> None:
|
||||
"""Set all devices to unavailable and tell Home Assistant."""
|
||||
@@ -222,8 +234,8 @@ class HomematicipHAP:
|
||||
async def async_reset(self) -> bool:
|
||||
"""Close the websocket connection."""
|
||||
self._ws_close_requested = True
|
||||
if self._retry_task is not None:
|
||||
self._retry_task.cancel()
|
||||
if self._get_state_task is not None:
|
||||
self._get_state_task.cancel()
|
||||
await self.home.disable_events_async()
|
||||
_LOGGER.debug("Closed connection to HomematicIP cloud server")
|
||||
await self.hass.config_entries.async_unload_platforms(
|
||||
@@ -247,7 +259,9 @@ class HomematicipHAP:
|
||||
"""Handle websocket connected."""
|
||||
_LOGGER.info("Websocket connection to HomematicIP Cloud established")
|
||||
if self._ws_connection_closed.is_set():
|
||||
await self.get_state()
|
||||
self._get_state_task = self.hass.async_create_task(self._try_get_state())
|
||||
self._get_state_task.add_done_callback(self.get_state_finished)
|
||||
|
||||
self._ws_connection_closed.clear()
|
||||
|
||||
async def ws_disconnected_handler(self) -> None:
|
||||
@@ -256,11 +270,12 @@ class HomematicipHAP:
|
||||
self._ws_connection_closed.set()
|
||||
|
||||
async def ws_reconnected_handler(self, reason: str) -> None:
|
||||
"""Handle websocket reconnection."""
|
||||
"""Handle websocket reconnection. Is called when Websocket tries to reconnect."""
|
||||
_LOGGER.info(
|
||||
"Websocket connection to HomematicIP Cloud re-established due to reason: %s",
|
||||
"Websocket connection to HomematicIP Cloud trying to reconnect due to reason: %s",
|
||||
reason,
|
||||
)
|
||||
|
||||
self._ws_connection_closed.set()
|
||||
|
||||
async def get_hap(
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homematicip_cloud",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["homematicip"],
|
||||
"requirements": ["homematicip==2.0.6"]
|
||||
"requirements": ["homematicip==2.0.7"]
|
||||
}
|
||||
|
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aioimmich"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["aioimmich==0.10.1"]
|
||||
"requirements": ["aioimmich==0.10.2"]
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ class ImmichUpdateEntity(ImmichEntity, UpdateEntity):
|
||||
return self.coordinator.data.server_about.version
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
def latest_version(self) -> str | None:
|
||||
"""Available new immich server version."""
|
||||
assert self.coordinator.data.server_version_check
|
||||
return self.coordinator.data.server_version_check.release_version
|
||||
|
@@ -23,7 +23,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
|
||||
from .const import CONF_USE_BLUETOOTH, DOMAIN
|
||||
from .coordinator import (
|
||||
@@ -57,11 +57,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: LaMarzoccoConfigEntry) -
|
||||
assert entry.unique_id
|
||||
serial = entry.unique_id
|
||||
|
||||
client = async_get_clientsession(hass)
|
||||
cloud_client = LaMarzoccoCloudClient(
|
||||
username=entry.data[CONF_USERNAME],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
client=client,
|
||||
client=async_create_clientsession(hass),
|
||||
)
|
||||
|
||||
try:
|
||||
|
@@ -66,7 +66,7 @@ ENTITIES: tuple[LaMarzoccoBinarySensorEntityDescription, ...] = (
|
||||
WidgetType.CM_BACK_FLUSH, BackFlush(status=BackFlushStatus.OFF)
|
||||
),
|
||||
).status
|
||||
is BackFlushStatus.REQUESTED
|
||||
in (BackFlushStatus.REQUESTED, BackFlushStatus.CLEANING)
|
||||
),
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
supported_fn=lambda coordinator: (
|
||||
|
@@ -33,7 +33,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant.helpers.selector import (
|
||||
SelectOptionDict,
|
||||
SelectSelector,
|
||||
@@ -83,7 +83,7 @@ class LmConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
**user_input,
|
||||
}
|
||||
|
||||
self._client = async_get_clientsession(self.hass)
|
||||
self._client = async_create_clientsession(self.hass)
|
||||
cloud_client = LaMarzoccoCloudClient(
|
||||
username=data[CONF_USERNAME],
|
||||
password=data[CONF_PASSWORD],
|
||||
|
@@ -37,5 +37,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pylamarzocco"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pylamarzocco==2.0.9"]
|
||||
"requirements": ["pylamarzocco==2.0.11"]
|
||||
}
|
||||
|
@@ -10,6 +10,9 @@ import aiolifx_effects as aiolifx_effects_module
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_BRIGHTNESS_STEP,
|
||||
ATTR_BRIGHTNESS_STEP_PCT,
|
||||
ATTR_EFFECT,
|
||||
ATTR_TRANSITION,
|
||||
LIGHT_TURN_ON_SCHEMA,
|
||||
@@ -234,6 +237,20 @@ class LIFXLight(LIFXEntity, LightEntity):
|
||||
else:
|
||||
fade = 0
|
||||
|
||||
if ATTR_BRIGHTNESS_STEP in kwargs or ATTR_BRIGHTNESS_STEP_PCT in kwargs:
|
||||
brightness = self.brightness if self.is_on and self.brightness else 0
|
||||
|
||||
if ATTR_BRIGHTNESS_STEP in kwargs:
|
||||
brightness += kwargs.pop(ATTR_BRIGHTNESS_STEP)
|
||||
|
||||
else:
|
||||
brightness_pct = round(brightness / 255 * 100)
|
||||
brightness = round(
|
||||
(brightness_pct + kwargs.pop(ATTR_BRIGHTNESS_STEP_PCT)) / 100 * 255
|
||||
)
|
||||
|
||||
kwargs[ATTR_BRIGHTNESS] = max(0, min(255, brightness))
|
||||
|
||||
# These are both False if ATTR_POWER is not set
|
||||
power_on = kwargs.get(ATTR_POWER, False)
|
||||
power_off = not kwargs.get(ATTR_POWER, True)
|
||||
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from chip.clusters import Objects as clusters
|
||||
from chip.clusters.Objects import NullValue
|
||||
from matter_server.client.models import device_types
|
||||
|
||||
from homeassistant.components.light import (
|
||||
@@ -241,7 +242,7 @@ class MatterLight(MatterEntity, LightEntity):
|
||||
|
||||
return int(color_temp)
|
||||
|
||||
def _get_brightness(self) -> int:
|
||||
def _get_brightness(self) -> int | None:
|
||||
"""Get brightness from matter."""
|
||||
|
||||
level_control = self._endpoint.get_cluster(clusters.LevelControl)
|
||||
@@ -255,6 +256,10 @@ class MatterLight(MatterEntity, LightEntity):
|
||||
self.entity_id,
|
||||
)
|
||||
|
||||
if level_control.currentLevel is NullValue:
|
||||
# currentLevel is a nullable value.
|
||||
return None
|
||||
|
||||
return round(
|
||||
renormalize(
|
||||
level_control.currentLevel,
|
||||
|
@@ -26,14 +26,6 @@ class OAuth2FlowHandler(
|
||||
"""Return logger."""
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
@property
|
||||
def extra_authorize_data(self) -> dict:
|
||||
"""Extra data that needs to be appended to the authorize url."""
|
||||
# "vg" is mandatory but the value doesn't seem to matter
|
||||
return {
|
||||
"vg": "sv-SE",
|
||||
}
|
||||
|
||||
async def async_step_reauth(
|
||||
self, entry_data: Mapping[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
|
@@ -21,5 +21,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/motion_blinds",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["motionblinds"],
|
||||
"requirements": ["motionblinds==0.6.28"]
|
||||
"requirements": ["motionblinds==0.6.29"]
|
||||
}
|
||||
|
@@ -2114,6 +2114,9 @@ def data_schema_from_fields(
|
||||
if schema_section is None:
|
||||
data_schema.update(data_schema_element)
|
||||
continue
|
||||
if not data_schema_element:
|
||||
# Do not show empty sections
|
||||
continue
|
||||
collapsed = (
|
||||
not any(
|
||||
(default := data_schema_fields[str(option)].default) is vol.UNDEFINED
|
||||
|
@@ -389,16 +389,6 @@ def async_setup_entity_entry_helper(
|
||||
_async_setup_entities()
|
||||
|
||||
|
||||
def init_entity_id_from_config(
|
||||
hass: HomeAssistant, entity: Entity, config: ConfigType, entity_id_format: str
|
||||
) -> None:
|
||||
"""Set entity_id from object_id if defined in config."""
|
||||
if CONF_OBJECT_ID in config:
|
||||
entity.entity_id = async_generate_entity_id(
|
||||
entity_id_format, config[CONF_OBJECT_ID], None, hass
|
||||
)
|
||||
|
||||
|
||||
class MqttAttributesMixin(Entity):
|
||||
"""Mixin used for platforms that support JSON attributes."""
|
||||
|
||||
@@ -1312,6 +1302,7 @@ class MqttEntity(
|
||||
_attr_should_poll = False
|
||||
_default_name: str | None
|
||||
_entity_id_format: str
|
||||
_update_registry_entity_id: str | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1346,13 +1337,33 @@ class MqttEntity(
|
||||
|
||||
def _init_entity_id(self) -> None:
|
||||
"""Set entity_id from object_id if defined in config."""
|
||||
init_entity_id_from_config(
|
||||
self.hass, self, self._config, self._entity_id_format
|
||||
if CONF_OBJECT_ID not in self._config:
|
||||
return
|
||||
self.entity_id = async_generate_entity_id(
|
||||
self._entity_id_format, self._config[CONF_OBJECT_ID], None, self.hass
|
||||
)
|
||||
if self.unique_id is None:
|
||||
return
|
||||
# Check for previous deleted entities
|
||||
entity_registry = er.async_get(self.hass)
|
||||
entity_platform = self._entity_id_format.split(".")[0]
|
||||
if (
|
||||
deleted_entry := entity_registry.deleted_entities.get(
|
||||
(entity_platform, DOMAIN, self.unique_id)
|
||||
)
|
||||
) and deleted_entry.entity_id != self.entity_id:
|
||||
# Plan to update the entity_id basis on `object_id` if a deleted entity was found
|
||||
self._update_registry_entity_id = self.entity_id
|
||||
|
||||
@final
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to MQTT events."""
|
||||
if self._update_registry_entity_id is not None:
|
||||
entity_registry = er.async_get(self.hass)
|
||||
entity_registry.async_update_entity(
|
||||
self.entity_id, new_entity_id=self._update_registry_entity_id
|
||||
)
|
||||
|
||||
await super().async_added_to_hass()
|
||||
self._subscriptions = {}
|
||||
self._prepare_subscribe_topics()
|
||||
|
@@ -98,6 +98,12 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
|
||||
f"together with state class `{state_class}`"
|
||||
)
|
||||
|
||||
unit_of_measurement: str | None
|
||||
if (
|
||||
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
) is not None and not unit_of_measurement.strip():
|
||||
config.pop(CONF_UNIT_OF_MEASUREMENT)
|
||||
|
||||
# Only allow `options` to be set for `enum` sensors
|
||||
# to limit the possible sensor values
|
||||
if (options := config.get(CONF_OPTIONS)) is not None:
|
||||
|
@@ -7,6 +7,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/music_assistant",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["music_assistant"],
|
||||
"requirements": ["music-assistant-client==1.2.3"],
|
||||
"requirements": ["music-assistant-client==1.2.4"],
|
||||
"zeroconf": ["_mass._tcp.local."]
|
||||
}
|
||||
|
@@ -248,8 +248,6 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
||||
player = self.player
|
||||
active_queue = self.active_queue
|
||||
# update generic attributes
|
||||
if player.powered and active_queue is not None:
|
||||
self._attr_state = MediaPlayerState(active_queue.state.value)
|
||||
if player.powered and player.playback_state is not None:
|
||||
self._attr_state = MediaPlayerState(player.playback_state.value)
|
||||
else:
|
||||
|
@@ -39,6 +39,7 @@ class BinarySensor(CoilEntity, BinarySensorEntity):
|
||||
def __init__(self, coordinator: CoilCoordinator, coil: Coil) -> None:
|
||||
"""Initialize entity."""
|
||||
super().__init__(coordinator, coil, ENTITY_ID_FORMAT)
|
||||
self._on_value = coil.get_mapping_for(1)
|
||||
|
||||
def _async_read_coil(self, data: CoilData) -> None:
|
||||
self._attr_is_on = data.value == "ON"
|
||||
self._attr_is_on = data.value == self._on_value
|
||||
|
@@ -41,14 +41,16 @@ class Switch(CoilEntity, SwitchEntity):
|
||||
def __init__(self, coordinator: CoilCoordinator, coil: Coil) -> None:
|
||||
"""Initialize entity."""
|
||||
super().__init__(coordinator, coil, ENTITY_ID_FORMAT)
|
||||
self._on_value = coil.get_mapping_for(1)
|
||||
self._off_value = coil.get_mapping_for(0)
|
||||
|
||||
def _async_read_coil(self, data: CoilData) -> None:
|
||||
self._attr_is_on = data.value == "ON"
|
||||
self._attr_is_on = data.value == self._on_value
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity on."""
|
||||
await self._async_write_coil("ON")
|
||||
await self._async_write_coil(self._on_value)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity off."""
|
||||
await self._async_write_coil("OFF")
|
||||
await self._async_write_coil(self._off_value)
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/nyt_games",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["nyt_games==0.4.4"]
|
||||
"requirements": ["nyt_games==0.5.0"]
|
||||
}
|
||||
|
@@ -218,6 +218,9 @@ def _async_fix_device_id(
|
||||
for device_entry in device_entries:
|
||||
unique_id = str(next(iter(device_entry.identifiers))[1])
|
||||
device_entry_map[unique_id] = device_entry
|
||||
if unique_id.startswith(mac_address):
|
||||
# Already in the correct format
|
||||
continue
|
||||
if (suffix := unique_id.removeprefix(str(serial_number))) != unique_id:
|
||||
migrations[unique_id] = f"{mac_address}{suffix}"
|
||||
|
||||
|
12
homeassistant/components/remote_calendar/client.py
Normal file
12
homeassistant/components/remote_calendar/client.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""Specifies the parameter for the httpx download."""
|
||||
|
||||
from httpx import AsyncClient, Response, Timeout
|
||||
|
||||
|
||||
async def get_calendar(client: AsyncClient, url: str) -> Response:
|
||||
"""Make an HTTP GET request using Home Assistant's async HTTPX client with timeout."""
|
||||
return await client.get(
|
||||
url,
|
||||
follow_redirects=True,
|
||||
timeout=Timeout(5, read=30, write=5, pool=5),
|
||||
)
|
@@ -4,13 +4,14 @@ from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from httpx import HTTPError, InvalidURL
|
||||
from httpx import HTTPError, InvalidURL, TimeoutException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_URL
|
||||
from homeassistant.helpers.httpx_client import get_async_client
|
||||
|
||||
from .client import get_calendar
|
||||
from .const import CONF_CALENDAR_NAME, DOMAIN
|
||||
from .ics import InvalidIcsException, parse_calendar
|
||||
|
||||
@@ -49,7 +50,7 @@ class RemoteCalendarConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})
|
||||
client = get_async_client(self.hass)
|
||||
try:
|
||||
res = await client.get(user_input[CONF_URL], follow_redirects=True)
|
||||
res = await get_calendar(client, user_input[CONF_URL])
|
||||
if res.status_code == HTTPStatus.FORBIDDEN:
|
||||
errors["base"] = "forbidden"
|
||||
return self.async_show_form(
|
||||
@@ -58,9 +59,14 @@ class RemoteCalendarConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors=errors,
|
||||
)
|
||||
res.raise_for_status()
|
||||
except TimeoutException as err:
|
||||
errors["base"] = "timeout_connect"
|
||||
_LOGGER.debug(
|
||||
"A timeout error occurred: %s", str(err) or type(err).__name__
|
||||
)
|
||||
except (HTTPError, InvalidURL) as err:
|
||||
errors["base"] = "cannot_connect"
|
||||
_LOGGER.debug("An error occurred: %s", err)
|
||||
_LOGGER.debug("An error occurred: %s", str(err) or type(err).__name__)
|
||||
else:
|
||||
try:
|
||||
await parse_calendar(self.hass, res.text)
|
||||
|
@@ -3,7 +3,7 @@
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from httpx import HTTPError, InvalidURL
|
||||
from httpx import HTTPError, InvalidURL, TimeoutException
|
||||
from ical.calendar import Calendar
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -12,6 +12,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.httpx_client import get_async_client
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .client import get_calendar
|
||||
from .const import DOMAIN
|
||||
from .ics import InvalidIcsException, parse_calendar
|
||||
|
||||
@@ -36,7 +37,7 @@ class RemoteCalendarDataUpdateCoordinator(DataUpdateCoordinator[Calendar]):
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
name=f"{DOMAIN}_{config_entry.title}",
|
||||
update_interval=SCAN_INTERVAL,
|
||||
always_update=True,
|
||||
)
|
||||
@@ -46,13 +47,19 @@ class RemoteCalendarDataUpdateCoordinator(DataUpdateCoordinator[Calendar]):
|
||||
async def _async_update_data(self) -> Calendar:
|
||||
"""Update data from the url."""
|
||||
try:
|
||||
res = await self._client.get(self._url, follow_redirects=True)
|
||||
res = await get_calendar(self._client, self._url)
|
||||
res.raise_for_status()
|
||||
except TimeoutException as err:
|
||||
_LOGGER.debug("%s: %s", self._url, str(err) or type(err).__name__)
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="timeout",
|
||||
) from err
|
||||
except (HTTPError, InvalidURL) as err:
|
||||
_LOGGER.debug("%s: %s", self._url, str(err) or type(err).__name__)
|
||||
raise UpdateFailed(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="unable_to_fetch",
|
||||
translation_placeholders={"err": str(err)},
|
||||
) from err
|
||||
try:
|
||||
self.ics = res.text
|
||||
|
@@ -18,14 +18,18 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"error": {
|
||||
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"forbidden": "The server understood the request but refuses to authorize it.",
|
||||
"invalid_ics_file": "There was a problem reading the calendar information. See the error log for additional details."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"timeout": {
|
||||
"message": "The connection timed out. See the debug log for additional details."
|
||||
},
|
||||
"unable_to_fetch": {
|
||||
"message": "Unable to fetch calendar data: {err}"
|
||||
"message": "Unable to fetch calendar data. See the debug log for additional details."
|
||||
},
|
||||
"unable_to_parse": {
|
||||
"message": "Unable to parse calendar data: {err}"
|
||||
|
@@ -196,7 +196,7 @@ class RensonFan(RensonEntity, FanEntity):
|
||||
all_data = self.coordinator.data
|
||||
breeze_temp = self.api.get_field_value(all_data, BREEZE_TEMPERATURE_FIELD)
|
||||
await self.hass.async_add_executor_job(
|
||||
self.api.set_breeze, cmd.name, breeze_temp, True
|
||||
self.api.set_breeze, cmd, breeze_temp, True
|
||||
)
|
||||
else:
|
||||
await self.hass.async_add_executor_job(self.api.set_manual_level, cmd)
|
||||
|
@@ -49,7 +49,7 @@ class RestData:
|
||||
# Convert auth tuple to aiohttp.BasicAuth if needed
|
||||
if isinstance(auth, tuple) and len(auth) == 2:
|
||||
self._auth: aiohttp.BasicAuth | aiohttp.DigestAuthMiddleware | None = (
|
||||
aiohttp.BasicAuth(auth[0], auth[1])
|
||||
aiohttp.BasicAuth(auth[0], auth[1], encoding="utf-8")
|
||||
)
|
||||
else:
|
||||
self._auth = auth
|
||||
@@ -115,6 +115,16 @@ class RestData:
|
||||
for key, value in rendered_params.items():
|
||||
if isinstance(value, bool):
|
||||
rendered_params[key] = str(value).lower()
|
||||
elif not isinstance(value, (str, int, float, type(None))):
|
||||
# For backward compatibility with httpx behavior, convert non-primitive
|
||||
# types to strings. This maintains compatibility after switching from
|
||||
# httpx to aiohttp. See https://github.com/home-assistant/core/issues/148153
|
||||
_LOGGER.debug(
|
||||
"REST query parameter '%s' has type %s, converting to string",
|
||||
key,
|
||||
type(value).__name__,
|
||||
)
|
||||
rendered_params[key] = str(value)
|
||||
|
||||
_LOGGER.debug("Updating from %s", self._resource)
|
||||
# Create request kwargs
|
||||
@@ -140,7 +150,14 @@ class RestData:
|
||||
self._method, self._resource, **request_kwargs
|
||||
) as response:
|
||||
# Read the response
|
||||
self.data = await response.text(encoding=self._encoding)
|
||||
# Only use configured encoding if no charset in Content-Type header
|
||||
# If charset is present in Content-Type, let aiohttp use it
|
||||
if response.charset:
|
||||
# Let aiohttp use the charset from Content-Type header
|
||||
self.data = await response.text()
|
||||
else:
|
||||
# Use configured encoding as fallback
|
||||
self.data = await response.text(encoding=self._encoding)
|
||||
self.headers = response.headers
|
||||
|
||||
except TimeoutError as ex:
|
||||
|
@@ -178,6 +178,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
)
|
||||
|
||||
if not service.return_response:
|
||||
# always read the response to avoid closing the connection
|
||||
# before the server has finished sending it, while avoiding excessive memory usage
|
||||
async for _ in response.content.iter_chunked(1024):
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
_content = None
|
||||
|
@@ -124,6 +124,7 @@ class SamsungTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._model: str | None = None
|
||||
self._connect_result: str | None = None
|
||||
self._method: str | None = None
|
||||
self._port: int | None = None
|
||||
self._name: str | None = None
|
||||
self._title: str = ""
|
||||
self._id: int | None = None
|
||||
@@ -199,33 +200,37 @@ class SamsungTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def _async_create_bridge(self) -> None:
|
||||
"""Create the bridge."""
|
||||
result, method, _info = await self._async_get_device_info_and_method()
|
||||
result = await self._async_load_device_info()
|
||||
if result not in SUCCESSFUL_RESULTS:
|
||||
LOGGER.debug("No working config found for %s", self._host)
|
||||
raise AbortFlow(result)
|
||||
assert method is not None
|
||||
self._bridge = SamsungTVBridge.get_bridge(self.hass, method, self._host)
|
||||
assert self._method is not None
|
||||
self._bridge = SamsungTVBridge.get_bridge(
|
||||
self.hass, self._method, self._host, self._port
|
||||
)
|
||||
|
||||
async def _async_get_device_info_and_method(
|
||||
async def _async_load_device_info(
|
||||
self,
|
||||
) -> tuple[str, str | None, dict[str, Any] | None]:
|
||||
) -> str:
|
||||
"""Get device info and method only once."""
|
||||
if self._connect_result is None:
|
||||
result, _, method, info = await async_get_device_info(self.hass, self._host)
|
||||
result, port, method, info = await async_get_device_info(
|
||||
self.hass, self._host
|
||||
)
|
||||
self._connect_result = result
|
||||
self._method = method
|
||||
self._port = port
|
||||
self._device_info = info
|
||||
if not method:
|
||||
LOGGER.debug("Host:%s did not return device info", self._host)
|
||||
return result, None, None
|
||||
return self._connect_result, self._method, self._device_info
|
||||
return self._connect_result
|
||||
|
||||
async def _async_get_and_check_device_info(self) -> bool:
|
||||
"""Try to get the device info."""
|
||||
result, _method, info = await self._async_get_device_info_and_method()
|
||||
result = await self._async_load_device_info()
|
||||
if result not in SUCCESSFUL_RESULTS:
|
||||
raise AbortFlow(result)
|
||||
if not info:
|
||||
if not (info := self._device_info):
|
||||
return False
|
||||
dev_info = info.get("device", {})
|
||||
assert dev_info is not None
|
||||
|
@@ -40,7 +40,7 @@
|
||||
"samsungctl[websocket]==0.7.1",
|
||||
"samsungtvws[async,encrypted]==2.7.2",
|
||||
"wakeonlan==3.1.0",
|
||||
"async-upnp-client==0.44.0"
|
||||
"async-upnp-client==0.45.0"
|
||||
],
|
||||
"ssdp": [
|
||||
{
|
||||
|
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/schlage",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["pyschlage==2025.4.0"]
|
||||
"requirements": ["pyschlage==2025.7.2"]
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/sharkiq",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["sharkiq"],
|
||||
"requirements": ["sharkiq==1.1.0"]
|
||||
"requirements": ["sharkiq==1.1.1"]
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioshelly"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["aioshelly==13.7.1"],
|
||||
"requirements": ["aioshelly==13.7.2"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_http._tcp.local.",
|
||||
|
@@ -868,8 +868,8 @@ RPC_SENSORS: Final = {
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
available=lambda status: (status and status["n_current"]) is not None,
|
||||
removal_condition=lambda _config, status, _key: "n_current" not in status,
|
||||
removal_condition=lambda _config, status, key: status[key].get("n_current")
|
||||
is None,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"total_current": RpcSensorDescription(
|
||||
|
@@ -30,5 +30,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pysmartthings"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pysmartthings==3.2.5"]
|
||||
"requirements": ["pysmartthings==3.2.8"]
|
||||
}
|
||||
|
@@ -18,6 +18,11 @@ from .entity import SmartThingsEntity
|
||||
|
||||
LAMP_TO_HA = {
|
||||
"extraHigh": "extra_high",
|
||||
"high": "high",
|
||||
"mid": "mid",
|
||||
"low": "low",
|
||||
"on": "on",
|
||||
"off": "off",
|
||||
}
|
||||
|
||||
WASHER_SOIL_LEVEL_TO_HA = {
|
||||
|
@@ -12,7 +12,7 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["pysmlight==0.2.6"],
|
||||
"requirements": ["pysmlight==0.2.7"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_slzb-06._tcp.local."
|
||||
|
@@ -38,7 +38,7 @@ BINARY_SENSOR_DESCRIPTIONS: list[SnooBinarySensorEntityDescription] = [
|
||||
SnooBinarySensorEntityDescription(
|
||||
key="right_clip",
|
||||
translation_key="right_clip",
|
||||
value_fn=lambda data: data.left_safety_clip,
|
||||
value_fn=lambda data: data.right_safety_clip,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
|
@@ -72,7 +72,7 @@ class SonosFavorites(SonosHouseholdCoordinator):
|
||||
"""Process the event payload in an async lock and update entities."""
|
||||
event_id = event.variables["favorites_update_id"]
|
||||
container_ids = event.variables["container_update_i_ds"]
|
||||
if not (match := re.search(r"FV:2,(\d+)", container_ids)):
|
||||
if not container_ids or not (match := re.search(r"FV:2,(\d+)", container_ids)):
|
||||
return
|
||||
|
||||
container_id = int(match.groups()[0])
|
||||
|
@@ -221,12 +221,16 @@ def _get_item_thumbnail(
|
||||
) -> str | None:
|
||||
"""Construct path to thumbnail image."""
|
||||
item_thumbnail: str | None = None
|
||||
if artwork_track_id := item.get("artwork_track_id"):
|
||||
track_id = item.get("artwork_track_id") or (
|
||||
item.get("id") if item_type == "track" else None
|
||||
)
|
||||
|
||||
if track_id:
|
||||
if internal_request:
|
||||
item_thumbnail = player.generate_image_url_from_track_id(artwork_track_id)
|
||||
item_thumbnail = player.generate_image_url_from_track_id(track_id)
|
||||
elif item_type is not None:
|
||||
item_thumbnail = entity.get_browse_image_url(
|
||||
item_type, item["id"], artwork_track_id
|
||||
item_type, item["id"], track_id
|
||||
)
|
||||
|
||||
elif search_type in ["apps", "radios"]:
|
||||
@@ -311,8 +315,7 @@ async def build_item_response(
|
||||
title=item["title"],
|
||||
media_content_type=item_type,
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS[item_type]["item"],
|
||||
can_expand=CONTENT_TYPE_MEDIA_CLASS[item_type]["children"]
|
||||
is not None,
|
||||
can_expand=bool(CONTENT_TYPE_MEDIA_CLASS[item_type]["children"]),
|
||||
can_play=True,
|
||||
)
|
||||
|
||||
|
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["async_upnp_client"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["async-upnp-client==0.44.0"]
|
||||
"requirements": ["async-upnp-client==0.45.0"]
|
||||
}
|
||||
|
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pysuez", "regex"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pysuezV2==2.0.5"]
|
||||
"requirements": ["pysuezV2==2.0.7"]
|
||||
}
|
||||
|
@@ -41,5 +41,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["switchbot"],
|
||||
"quality_scale": "gold",
|
||||
"requirements": ["PySwitchbot==0.67.0"]
|
||||
"requirements": ["PySwitchbot==0.68.2"]
|
||||
}
|
||||
|
@@ -113,11 +113,11 @@ SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
),
|
||||
"Plug Mini (US)": (
|
||||
VOLTAGE_DESCRIPTION,
|
||||
CURRENT_DESCRIPTION_IN_A,
|
||||
CURRENT_DESCRIPTION_IN_MA,
|
||||
),
|
||||
"Plug Mini (JP)": (
|
||||
VOLTAGE_DESCRIPTION,
|
||||
CURRENT_DESCRIPTION_IN_A,
|
||||
CURRENT_DESCRIPTION_IN_MA,
|
||||
),
|
||||
"Hub 2": (
|
||||
TEMPERATURE_DESCRIPTION,
|
||||
|
@@ -28,6 +28,7 @@ class SyncthruCoordinator(DataUpdateCoordinator[SyncThru]):
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
config_entry=entry,
|
||||
update_interval=timedelta(seconds=30),
|
||||
)
|
||||
self.syncthru = SyncThru(
|
||||
|
@@ -374,9 +374,7 @@ class TelegramNotificationService:
|
||||
}
|
||||
if data is not None:
|
||||
if ATTR_PARSER in data:
|
||||
params[ATTR_PARSER] = self._parsers.get(
|
||||
data[ATTR_PARSER], self.parse_mode
|
||||
)
|
||||
params[ATTR_PARSER] = data[ATTR_PARSER]
|
||||
if ATTR_TIMEOUT in data:
|
||||
params[ATTR_TIMEOUT] = data[ATTR_TIMEOUT]
|
||||
if ATTR_DISABLE_NOTIF in data:
|
||||
@@ -408,6 +406,8 @@ class TelegramNotificationService:
|
||||
params[ATTR_REPLYMARKUP] = InlineKeyboardMarkup(
|
||||
[_make_row_inline_keyboard(row) for row in keys]
|
||||
)
|
||||
if params[ATTR_PARSER] == PARSER_PLAIN_TEXT:
|
||||
params[ATTR_PARSER] = None
|
||||
return params
|
||||
|
||||
async def _send_msg(
|
||||
|
@@ -159,8 +159,6 @@ class OptionsFlowHandler(OptionsFlow):
|
||||
"""Manage the options."""
|
||||
|
||||
if user_input is not None:
|
||||
if user_input[ATTR_PARSER] == PARSER_PLAIN_TEXT:
|
||||
user_input[ATTR_PARSER] = None
|
||||
return self.async_create_entry(data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
|
@@ -109,6 +109,7 @@ send_photo:
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
disable_notification:
|
||||
selector:
|
||||
boolean:
|
||||
@@ -261,6 +262,7 @@ send_animation:
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
disable_notification:
|
||||
selector:
|
||||
boolean:
|
||||
@@ -341,6 +343,7 @@ send_video:
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
disable_notification:
|
||||
selector:
|
||||
boolean:
|
||||
@@ -493,6 +496,7 @@ send_document:
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
disable_notification:
|
||||
selector:
|
||||
boolean:
|
||||
@@ -670,6 +674,7 @@ edit_message:
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
disable_web_page_preview:
|
||||
selector:
|
||||
boolean:
|
||||
|
@@ -82,7 +82,7 @@ class PushBot(BaseTelegramBot):
|
||||
self.base_url = config.data.get(CONF_URL) or get_url(
|
||||
hass, require_ssl=True, allow_internal=False
|
||||
)
|
||||
self.webhook_url = f"{self.base_url}{TELEGRAM_WEBHOOK_URL}"
|
||||
self.webhook_url = self.base_url + _get_webhook_url(bot)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""Shutdown the app."""
|
||||
@@ -98,9 +98,11 @@ class PushBot(BaseTelegramBot):
|
||||
api_kwargs={"secret_token": self.secret_token},
|
||||
connect_timeout=5,
|
||||
)
|
||||
except TelegramError:
|
||||
except TelegramError as err:
|
||||
retry_num += 1
|
||||
_LOGGER.warning("Error trying to set webhook (retry #%d)", retry_num)
|
||||
_LOGGER.warning(
|
||||
"Error trying to set webhook (retry #%d)", retry_num, exc_info=err
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
@@ -143,7 +145,6 @@ class PushBotView(HomeAssistantView):
|
||||
"""View for handling webhook calls from Telegram."""
|
||||
|
||||
requires_auth = False
|
||||
url = TELEGRAM_WEBHOOK_URL
|
||||
name = "telegram_webhooks"
|
||||
|
||||
def __init__(
|
||||
@@ -160,6 +161,7 @@ class PushBotView(HomeAssistantView):
|
||||
self.application = application
|
||||
self.trusted_networks = trusted_networks
|
||||
self.secret_token = secret_token
|
||||
self.url = _get_webhook_url(bot)
|
||||
|
||||
async def post(self, request: HomeAssistantRequest) -> Response | None:
|
||||
"""Accept the POST from telegram."""
|
||||
@@ -183,3 +185,7 @@ class PushBotView(HomeAssistantView):
|
||||
await self.application.process_update(update)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _get_webhook_url(bot: Bot) -> str:
|
||||
return f"{TELEGRAM_WEBHOOK_URL}_{bot.id}"
|
||||
|
@@ -14,9 +14,8 @@ CONF_REFRESH_TOKEN = "refresh_token"
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
CLIENT_ID = "71b813eb-4a2e-483a-b831-4dec5cb9bf0d"
|
||||
AUTHORIZE_URL = "https://auth.tesla.com/oauth2/v3/authorize"
|
||||
TOKEN_URL = "https://auth.tesla.com/oauth2/v3/token"
|
||||
AUTHORIZE_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize"
|
||||
TOKEN_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token"
|
||||
|
||||
SCOPES = [
|
||||
Scope.OPENID,
|
||||
|
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tesla_fleet",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tesla-fleet-api"],
|
||||
"requirements": ["tesla-fleet-api==1.2.0"]
|
||||
"requirements": ["tesla-fleet-api==1.2.2"]
|
||||
}
|
||||
|
@@ -125,8 +125,8 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetryBinarySensorEntityDescription, ...] = (
|
||||
key="charge_state_conn_charge_cable",
|
||||
polling=True,
|
||||
polling_value_fn=lambda x: x != "<invalid>",
|
||||
streaming_listener=lambda vehicle, callback: vehicle.listen_ChargingCableType(
|
||||
lambda value: callback(value is not None and value != "Unknown")
|
||||
streaming_listener=lambda vehicle, callback: vehicle.listen_DetailedChargeState(
|
||||
lambda value: callback(None if value is None else value != "Disconnected")
|
||||
),
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
|
@@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import TeslemetryConfigEntry
|
||||
from .entity import TeslemetryVehiclePollingEntity
|
||||
from .entity import TeslemetryVehicleStreamEntity
|
||||
from .helpers import handle_command, handle_vehicle_command
|
||||
from .models import TeslemetryVehicleData
|
||||
|
||||
@@ -74,7 +74,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class TeslemetryButtonEntity(TeslemetryVehiclePollingEntity, ButtonEntity):
|
||||
class TeslemetryButtonEntity(TeslemetryVehicleStreamEntity, ButtonEntity):
|
||||
"""Base class for Teslemetry buttons."""
|
||||
|
||||
api: Vehicle
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tesla-fleet-api"],
|
||||
"requirements": ["tesla-fleet-api==1.2.0", "teslemetry-stream==0.7.9"]
|
||||
"requirements": ["tesla-fleet-api==1.2.2", "teslemetry-stream==0.7.9"]
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tessie",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tessie", "tesla-fleet-api"],
|
||||
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.2.0"]
|
||||
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.2.2"]
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiounifi"],
|
||||
"requirements": ["aiounifi==83"],
|
||||
"requirements": ["aiounifi==84"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
@@ -40,7 +40,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["uiprotect", "unifi_discovery"],
|
||||
"requirements": ["uiprotect==7.14.1", "unifi-discovery==1.2.0"],
|
||||
"requirements": ["uiprotect==7.14.2", "unifi-discovery==1.2.0"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["async_upnp_client"],
|
||||
"requirements": ["async-upnp-client==0.44.0", "getmac==0.9.5"],
|
||||
"requirements": ["async-upnp-client==0.45.0", "getmac==0.9.5"],
|
||||
"ssdp": [
|
||||
{
|
||||
"st": "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/venstar",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["venstarcolortouch"],
|
||||
"requirements": ["venstarcolortouch==0.19"]
|
||||
"requirements": ["venstarcolortouch==0.21"]
|
||||
}
|
||||
|
@@ -11,5 +11,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/vicare",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["PyViCare"],
|
||||
"requirements": ["PyViCare==2.44.0"]
|
||||
"requirements": ["PyViCare==2.50.0"]
|
||||
}
|
||||
|
@@ -98,7 +98,10 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
data = {CONF_HOST: self._host, CONF_CLIENT_SECRET: client.client_key}
|
||||
|
||||
if not self._name:
|
||||
self._name = f"{DEFAULT_NAME} {client.tv_info.system['modelName']}"
|
||||
if model_name := client.tv_info.system.get("modelName"):
|
||||
self._name = f"{DEFAULT_NAME} {model_name}"
|
||||
else:
|
||||
self._name = DEFAULT_NAME
|
||||
return self.async_create_entry(title=self._name, data=data)
|
||||
|
||||
return self.async_show_form(step_id="pairing", errors=errors)
|
||||
|
@@ -6,7 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/webostv",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiowebostv"],
|
||||
"requirements": ["aiowebostv==0.7.3"],
|
||||
"requirements": ["aiowebostv==0.7.4"],
|
||||
"ssdp": [
|
||||
{
|
||||
"st": "urn:lge-com:service:webos-second-screen:1"
|
||||
|
@@ -23,7 +23,7 @@ async def async_setup_entry(
|
||||
entities: list[WebControlProGenericEntity] = [
|
||||
WebControlProIdentifyButton(config_entry.entry_id, dest)
|
||||
for dest in hub.dests.values()
|
||||
if dest.action(WMS_WebControl_pro_API_actionDescription.Identify)
|
||||
if dest.hasAction(WMS_WebControl_pro_API_actionDescription.Identify)
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
|
@@ -32,9 +32,9 @@ async def async_setup_entry(
|
||||
|
||||
entities: list[WebControlProGenericEntity] = []
|
||||
for dest in hub.dests.values():
|
||||
if dest.action(WMS_WebControl_pro_API_actionDescription.AwningDrive):
|
||||
if dest.hasAction(WMS_WebControl_pro_API_actionDescription.AwningDrive):
|
||||
entities.append(WebControlProAwning(config_entry.entry_id, dest))
|
||||
elif dest.action(
|
||||
elif dest.hasAction(
|
||||
WMS_WebControl_pro_API_actionDescription.RollerShutterBlindDrive
|
||||
):
|
||||
entities.append(WebControlProRollerShutter(config_entry.entry_id, dest))
|
||||
|
@@ -33,9 +33,9 @@ async def async_setup_entry(
|
||||
|
||||
entities: list[WebControlProGenericEntity] = []
|
||||
for dest in hub.dests.values():
|
||||
if dest.action(WMS_WebControl_pro_API_actionDescription.LightDimming):
|
||||
if dest.hasAction(WMS_WebControl_pro_API_actionDescription.LightDimming):
|
||||
entities.append(WebControlProDimmer(config_entry.entry_id, dest))
|
||||
elif dest.action(WMS_WebControl_pro_API_actionDescription.LightSwitch):
|
||||
elif dest.hasAction(WMS_WebControl_pro_API_actionDescription.LightSwitch):
|
||||
entities.append(WebControlProLight(config_entry.entry_id, dest))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
@@ -14,5 +14,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/wmspro",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["pywmspro==0.3.0"]
|
||||
"requirements": ["pywmspro==0.3.2"]
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pyasn1", "slixmpp"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["slixmpp==1.8.5", "emoji==2.8.0"]
|
||||
"requirements": ["slixmpp==1.10.0", "emoji==2.8.0"]
|
||||
}
|
||||
|
@@ -144,7 +144,8 @@ async def async_send_message( # noqa: C901
|
||||
|
||||
self.loop = hass.loop
|
||||
|
||||
self.force_starttls = use_tls
|
||||
self.enable_starttls = use_tls
|
||||
self.enable_direct_tls = use_tls
|
||||
self.use_ipv6 = False
|
||||
self.add_event_handler("failed_all_auth", self.disconnect_on_login_fail)
|
||||
self.add_event_handler("session_start", self.start)
|
||||
@@ -163,7 +164,7 @@ async def async_send_message( # noqa: C901
|
||||
self.register_plugin("xep_0128") # Service Discovery
|
||||
self.register_plugin("xep_0363") # HTTP upload
|
||||
|
||||
self.connect(force_starttls=self.force_starttls, use_ssl=False)
|
||||
self.connect()
|
||||
|
||||
async def start(self, event):
|
||||
"""Start the communication and sends the message."""
|
||||
|
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["async_upnp_client", "yeelight"],
|
||||
"requirements": ["yeelight==0.7.16", "async-upnp-client==0.44.0"],
|
||||
"requirements": ["yeelight==0.7.16", "async-upnp-client==0.45.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_miio._udp.local.",
|
||||
|
@@ -21,7 +21,7 @@
|
||||
"zha",
|
||||
"universal_silabs_flasher"
|
||||
],
|
||||
"requirements": ["zha==0.0.61"],
|
||||
"requirements": ["zha==0.0.62"],
|
||||
"usb": [
|
||||
{
|
||||
"vid": "10C4",
|
||||
|
@@ -495,10 +495,23 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
self._usb_discovery = True
|
||||
if current_config_entries:
|
||||
return await self.async_step_intent_migrate()
|
||||
return await self.async_step_confirm_usb_migration()
|
||||
|
||||
return await self.async_step_installation_type()
|
||||
|
||||
async def async_step_confirm_usb_migration(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Confirm USB migration."""
|
||||
if user_input is not None:
|
||||
return await self.async_step_intent_migrate()
|
||||
return self.async_show_form(
|
||||
step_id="confirm_usb_migration",
|
||||
description_placeholders={
|
||||
"usb_title": self.context["title_placeholders"][CONF_NAME],
|
||||
},
|
||||
)
|
||||
|
||||
async def async_step_manual(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from zwave_js_server.const import NodeStatus
|
||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import (
|
||||
@@ -27,8 +26,6 @@ from .discovery import ZwaveDiscoveryInfo
|
||||
from .helpers import get_device_id, get_unique_id, get_valueless_base_unique_id
|
||||
|
||||
EVENT_VALUE_REMOVED = "value removed"
|
||||
EVENT_DEAD = "dead"
|
||||
EVENT_ALIVE = "alive"
|
||||
|
||||
|
||||
class ZWaveBaseEntity(Entity):
|
||||
@@ -141,11 +138,6 @@ class ZWaveBaseEntity(Entity):
|
||||
)
|
||||
)
|
||||
|
||||
for status_event in (EVENT_ALIVE, EVENT_DEAD):
|
||||
self.async_on_remove(
|
||||
self.info.node.on(status_event, self._node_status_alive_or_dead)
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
@@ -211,19 +203,7 @@ class ZWaveBaseEntity(Entity):
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return entity availability."""
|
||||
return (
|
||||
self.driver.client.connected
|
||||
and bool(self.info.node.ready)
|
||||
and self.info.node.status != NodeStatus.DEAD
|
||||
)
|
||||
|
||||
@callback
|
||||
def _node_status_alive_or_dead(self, event_data: dict) -> None:
|
||||
"""Call when node status changes to alive or dead.
|
||||
|
||||
Should not be overridden by subclasses.
|
||||
"""
|
||||
self.async_write_ha_state()
|
||||
return self.driver.client.connected and bool(self.info.node.ready)
|
||||
|
||||
@callback
|
||||
def _value_changed(self, event_data: dict) -> None:
|
||||
|
@@ -108,6 +108,10 @@
|
||||
"start_addon": {
|
||||
"title": "Configuring add-on"
|
||||
},
|
||||
"confirm_usb_migration": {
|
||||
"description": "You are about to migrate your Z-Wave network from the old adapter to the new adapter {usb_title}. This will take a backup of the network from the old adapter and restore the network to the new adapter.\n\nPress Submit to continue with the migration.",
|
||||
"title": "Migrate to a new adapter"
|
||||
},
|
||||
"zeroconf_confirm": {
|
||||
"description": "Do you want to add the Z-Wave Server with home ID {home_id} found at {url} to Home Assistant?",
|
||||
"title": "Discovered Z-Wave Server"
|
||||
|
@@ -200,18 +200,13 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
||||
)
|
||||
return
|
||||
|
||||
# If device is asleep/dead, wait for it to wake up/become alive before
|
||||
# attempting an update
|
||||
for status, event_name in (
|
||||
(NodeStatus.ASLEEP, "wake up"),
|
||||
(NodeStatus.DEAD, "alive"),
|
||||
):
|
||||
if self.node.status == status:
|
||||
if not self._status_unsub:
|
||||
self._status_unsub = self.node.once(
|
||||
event_name, self._update_on_status_change
|
||||
)
|
||||
return
|
||||
# If device is asleep, wait for it to wake up before attempting an update
|
||||
if self.node.status == NodeStatus.ASLEEP:
|
||||
if not self._status_unsub:
|
||||
self._status_unsub = self.node.once(
|
||||
"wake up", self._update_on_status_change
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Retrieve all firmware updates including non-stable ones but filter
|
||||
|
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2025
|
||||
MINOR_VERSION: Final = 7
|
||||
PATCH_VERSION: Final = "0"
|
||||
PATCH_VERSION: Final = "4"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 2)
|
||||
|
@@ -6,14 +6,14 @@ aiodns==3.5.0
|
||||
aiohasupervisor==0.3.1
|
||||
aiohttp-asyncmdnsresolver==0.1.1
|
||||
aiohttp-fast-zlib==0.3.0
|
||||
aiohttp==3.12.13
|
||||
aiohttp==3.12.14
|
||||
aiohttp_cors==0.8.1
|
||||
aiousbwatcher==1.1.1
|
||||
aiozoneinfo==0.2.3
|
||||
annotatedyaml==0.4.5
|
||||
astral==2.2
|
||||
async-interrupt==1.2.2
|
||||
async-upnp-client==0.44.0
|
||||
async-upnp-client==0.45.0
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
attrs==25.3.0
|
||||
audioop-lts==0.2.1
|
||||
@@ -35,10 +35,10 @@ fnv-hash-fast==1.5.0
|
||||
go2rtc-client==0.2.1
|
||||
ha-ffmpeg==3.2.2
|
||||
habluetooth==3.49.0
|
||||
hass-nabucasa==0.104.0
|
||||
hass-nabucasa==0.106.0
|
||||
hassil==2.2.3
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-frontend==20250702.0
|
||||
home-assistant-frontend==20250702.3
|
||||
home-assistant-intents==2025.6.23
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2025.7.0"
|
||||
version = "2025.7.4"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
@@ -28,7 +28,7 @@ dependencies = [
|
||||
# change behavior based on presence of supervisor. Deprecated with #127228
|
||||
# Lib can be removed with 2025.11
|
||||
"aiohasupervisor==0.3.1",
|
||||
"aiohttp==3.12.13",
|
||||
"aiohttp==3.12.14",
|
||||
"aiohttp_cors==0.8.1",
|
||||
"aiohttp-fast-zlib==0.3.0",
|
||||
"aiohttp-asyncmdnsresolver==0.1.1",
|
||||
@@ -47,7 +47,7 @@ dependencies = [
|
||||
"fnv-hash-fast==1.5.0",
|
||||
# hass-nabucasa is imported by helpers which don't depend on the cloud
|
||||
# integration
|
||||
"hass-nabucasa==0.104.0",
|
||||
"hass-nabucasa==0.106.0",
|
||||
# When bumping httpx, please check the version pins of
|
||||
# httpcore, anyio, and h11 in gen_requirements_all
|
||||
"httpx==0.28.1",
|
||||
|
4
requirements.txt
generated
4
requirements.txt
generated
@@ -5,7 +5,7 @@
|
||||
# Home Assistant Core
|
||||
aiodns==3.5.0
|
||||
aiohasupervisor==0.3.1
|
||||
aiohttp==3.12.13
|
||||
aiohttp==3.12.14
|
||||
aiohttp_cors==0.8.1
|
||||
aiohttp-fast-zlib==0.3.0
|
||||
aiohttp-asyncmdnsresolver==0.1.1
|
||||
@@ -22,7 +22,7 @@ certifi>=2021.5.30
|
||||
ciso8601==2.3.2
|
||||
cronsim==2.6
|
||||
fnv-hash-fast==1.5.0
|
||||
hass-nabucasa==0.104.0
|
||||
hass-nabucasa==0.106.0
|
||||
httpx==0.28.1
|
||||
home-assistant-bluetooth==1.13.1
|
||||
ifaddr==0.2.0
|
||||
|
58
requirements_all.txt
generated
58
requirements_all.txt
generated
@@ -84,7 +84,7 @@ PyQRCode==1.2.1
|
||||
PyRMVtransport==0.3.3
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.67.0
|
||||
PySwitchbot==0.68.2
|
||||
|
||||
# homeassistant.components.switchmate
|
||||
PySwitchmate==0.5.1
|
||||
@@ -100,7 +100,7 @@ PyTransportNSW==0.1.1
|
||||
PyTurboJPEG==1.8.0
|
||||
|
||||
# homeassistant.components.vicare
|
||||
PyViCare==2.44.0
|
||||
PyViCare==2.50.0
|
||||
|
||||
# homeassistant.components.xiaomi_aqara
|
||||
PyXiaomiGateway==0.14.3
|
||||
@@ -185,7 +185,7 @@ aioairzone-cloud==0.6.12
|
||||
aioairzone==1.0.0
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==3.2.2
|
||||
aioamazondevices==3.5.1
|
||||
|
||||
# homeassistant.components.ambient_network
|
||||
# homeassistant.components.ambient_station
|
||||
@@ -283,7 +283,7 @@ aiohue==4.7.4
|
||||
aioimaplib==2.0.1
|
||||
|
||||
# homeassistant.components.immich
|
||||
aioimmich==0.10.1
|
||||
aioimmich==0.10.2
|
||||
|
||||
# homeassistant.components.apache_kafka
|
||||
aiokafka==0.10.0
|
||||
@@ -381,7 +381,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==13.7.1
|
||||
aioshelly==13.7.2
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
@@ -414,7 +414,7 @@ aiotedee==0.2.25
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==83
|
||||
aiounifi==84
|
||||
|
||||
# homeassistant.components.usb
|
||||
aiousbwatcher==1.1.1
|
||||
@@ -435,7 +435,7 @@ aiowatttime==0.1.1
|
||||
aiowebdav2==0.4.6
|
||||
|
||||
# homeassistant.components.webostv
|
||||
aiowebostv==0.7.3
|
||||
aiowebostv==0.7.4
|
||||
|
||||
# homeassistant.components.withings
|
||||
aiowithings==3.1.6
|
||||
@@ -527,7 +527,7 @@ asmog==0.0.6
|
||||
# homeassistant.components.ssdp
|
||||
# homeassistant.components.upnp
|
||||
# homeassistant.components.yeelight
|
||||
async-upnp-client==0.44.0
|
||||
async-upnp-client==0.45.0
|
||||
|
||||
# homeassistant.components.arve
|
||||
asyncarve==0.1.1
|
||||
@@ -1020,7 +1020,7 @@ georss-qld-bushfire-alert-client==0.8
|
||||
getmac==0.9.5
|
||||
|
||||
# homeassistant.components.gios
|
||||
gios==6.0.0
|
||||
gios==6.1.2
|
||||
|
||||
# homeassistant.components.gitter
|
||||
gitterpy==0.1.7
|
||||
@@ -1121,13 +1121,13 @@ ha-philipsjs==3.2.2
|
||||
ha-silabs-firmware-client==0.2.0
|
||||
|
||||
# homeassistant.components.habitica
|
||||
habiticalib==0.4.0
|
||||
habiticalib==0.4.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==3.49.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.104.0
|
||||
hass-nabucasa==0.106.0
|
||||
|
||||
# homeassistant.components.splunk
|
||||
hass-splunk==0.1.1
|
||||
@@ -1168,13 +1168,13 @@ hole==0.8.0
|
||||
holidays==0.75
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20250702.0
|
||||
home-assistant-frontend==20250702.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2025.6.23
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==2.0.6
|
||||
homematicip==2.0.7
|
||||
|
||||
# homeassistant.components.horizon
|
||||
horimote==0.4.1
|
||||
@@ -1452,7 +1452,7 @@ monzopy==1.4.2
|
||||
mopeka-iot-ble==0.8.0
|
||||
|
||||
# homeassistant.components.motion_blinds
|
||||
motionblinds==0.6.28
|
||||
motionblinds==0.6.29
|
||||
|
||||
# homeassistant.components.motionblinds_ble
|
||||
motionblindsble==0.1.3
|
||||
@@ -1467,7 +1467,7 @@ mozart-api==4.1.1.116.4
|
||||
mullvad-api==1.0.0
|
||||
|
||||
# homeassistant.components.music_assistant
|
||||
music-assistant-client==1.2.3
|
||||
music-assistant-client==1.2.4
|
||||
|
||||
# homeassistant.components.tts
|
||||
mutagen==1.47.0
|
||||
@@ -1555,7 +1555,7 @@ numato-gpio==0.13.0
|
||||
numpy==2.3.0
|
||||
|
||||
# homeassistant.components.nyt_games
|
||||
nyt_games==0.4.4
|
||||
nyt_games==0.5.0
|
||||
|
||||
# homeassistant.components.oasa_telematics
|
||||
oasatelematics==0.3
|
||||
@@ -1962,7 +1962,7 @@ pyeiscp==0.0.7
|
||||
pyemoncms==0.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==2.1.0
|
||||
pyenphase==2.2.2
|
||||
|
||||
# homeassistant.components.envisalink
|
||||
pyenvisalink==4.7
|
||||
@@ -2100,7 +2100,7 @@ pykwb==0.0.8
|
||||
pylacrosse==0.4
|
||||
|
||||
# homeassistant.components.lamarzocco
|
||||
pylamarzocco==2.0.9
|
||||
pylamarzocco==2.0.11
|
||||
|
||||
# homeassistant.components.lastfm
|
||||
pylast==5.1.0
|
||||
@@ -2309,7 +2309,7 @@ pysabnzbd==1.1.1
|
||||
pysaj==0.0.16
|
||||
|
||||
# homeassistant.components.schlage
|
||||
pyschlage==2025.4.0
|
||||
pyschlage==2025.7.2
|
||||
|
||||
# homeassistant.components.sensibo
|
||||
pysensibo==1.2.1
|
||||
@@ -2348,7 +2348,7 @@ pysmappee==0.2.29
|
||||
pysmarlaapi==0.9.0
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==3.2.5
|
||||
pysmartthings==3.2.8
|
||||
|
||||
# homeassistant.components.smarty
|
||||
pysmarty2==0.10.2
|
||||
@@ -2360,7 +2360,7 @@ pysmhi==1.0.2
|
||||
pysml==0.1.5
|
||||
|
||||
# homeassistant.components.smlight
|
||||
pysmlight==0.2.6
|
||||
pysmlight==0.2.7
|
||||
|
||||
# homeassistant.components.snmp
|
||||
pysnmp==6.2.6
|
||||
@@ -2384,7 +2384,7 @@ pysqueezebox==0.12.1
|
||||
pystiebeleltron==0.1.0
|
||||
|
||||
# homeassistant.components.suez_water
|
||||
pysuezV2==2.0.5
|
||||
pysuezV2==2.0.7
|
||||
|
||||
# homeassistant.components.switchbee
|
||||
pyswitchbee==1.8.3
|
||||
@@ -2596,7 +2596,7 @@ pywilight==0.0.74
|
||||
pywizlight==0.6.3
|
||||
|
||||
# homeassistant.components.wmspro
|
||||
pywmspro==0.3.0
|
||||
pywmspro==0.3.2
|
||||
|
||||
# homeassistant.components.ws66i
|
||||
pyws66i==1.1
|
||||
@@ -2756,7 +2756,7 @@ sentry-sdk==1.45.1
|
||||
sfrbox-api==0.0.11
|
||||
|
||||
# homeassistant.components.sharkiq
|
||||
sharkiq==1.1.0
|
||||
sharkiq==1.1.1
|
||||
|
||||
# homeassistant.components.aquostv
|
||||
sharp_aquos_rc==0.3.2
|
||||
@@ -2786,7 +2786,7 @@ skyboxremote==0.0.6
|
||||
slack_sdk==3.33.4
|
||||
|
||||
# homeassistant.components.xmpp
|
||||
slixmpp==1.8.5
|
||||
slixmpp==1.10.0
|
||||
|
||||
# homeassistant.components.smart_meter_texas
|
||||
smart-meter-texas==0.5.5
|
||||
@@ -2904,7 +2904,7 @@ temperusb==1.6.1
|
||||
# homeassistant.components.tesla_fleet
|
||||
# homeassistant.components.teslemetry
|
||||
# homeassistant.components.tessie
|
||||
tesla-fleet-api==1.2.0
|
||||
tesla-fleet-api==1.2.2
|
||||
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.5.2
|
||||
@@ -2994,7 +2994,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.14.1
|
||||
uiprotect==7.14.2
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
@@ -3041,7 +3041,7 @@ vehicle==2.2.2
|
||||
velbus-aio==2025.5.0
|
||||
|
||||
# homeassistant.components.venstar
|
||||
venstarcolortouch==0.19
|
||||
venstarcolortouch==0.21
|
||||
|
||||
# homeassistant.components.vilfo
|
||||
vilfo-api-client==0.5.0
|
||||
@@ -3190,7 +3190,7 @@ zeroconf==0.147.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.61
|
||||
zha==0.0.62
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.13
|
||||
|
56
requirements_test_all.txt
generated
56
requirements_test_all.txt
generated
@@ -81,7 +81,7 @@ PyQRCode==1.2.1
|
||||
PyRMVtransport==0.3.3
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.67.0
|
||||
PySwitchbot==0.68.2
|
||||
|
||||
# homeassistant.components.syncthru
|
||||
PySyncThru==0.8.0
|
||||
@@ -94,7 +94,7 @@ PyTransportNSW==0.1.1
|
||||
PyTurboJPEG==1.8.0
|
||||
|
||||
# homeassistant.components.vicare
|
||||
PyViCare==2.44.0
|
||||
PyViCare==2.50.0
|
||||
|
||||
# homeassistant.components.xiaomi_aqara
|
||||
PyXiaomiGateway==0.14.3
|
||||
@@ -173,7 +173,7 @@ aioairzone-cloud==0.6.12
|
||||
aioairzone==1.0.0
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==3.2.2
|
||||
aioamazondevices==3.5.1
|
||||
|
||||
# homeassistant.components.ambient_network
|
||||
# homeassistant.components.ambient_station
|
||||
@@ -268,7 +268,7 @@ aiohue==4.7.4
|
||||
aioimaplib==2.0.1
|
||||
|
||||
# homeassistant.components.immich
|
||||
aioimmich==0.10.1
|
||||
aioimmich==0.10.2
|
||||
|
||||
# homeassistant.components.apache_kafka
|
||||
aiokafka==0.10.0
|
||||
@@ -363,7 +363,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==13.7.1
|
||||
aioshelly==13.7.2
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
@@ -396,7 +396,7 @@ aiotedee==0.2.25
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==83
|
||||
aiounifi==84
|
||||
|
||||
# homeassistant.components.usb
|
||||
aiousbwatcher==1.1.1
|
||||
@@ -417,7 +417,7 @@ aiowatttime==0.1.1
|
||||
aiowebdav2==0.4.6
|
||||
|
||||
# homeassistant.components.webostv
|
||||
aiowebostv==0.7.3
|
||||
aiowebostv==0.7.4
|
||||
|
||||
# homeassistant.components.withings
|
||||
aiowithings==3.1.6
|
||||
@@ -491,7 +491,7 @@ arcam-fmj==1.8.1
|
||||
# homeassistant.components.ssdp
|
||||
# homeassistant.components.upnp
|
||||
# homeassistant.components.yeelight
|
||||
async-upnp-client==0.44.0
|
||||
async-upnp-client==0.45.0
|
||||
|
||||
# homeassistant.components.arve
|
||||
asyncarve==0.1.1
|
||||
@@ -890,7 +890,7 @@ georss-qld-bushfire-alert-client==0.8
|
||||
getmac==0.9.5
|
||||
|
||||
# homeassistant.components.gios
|
||||
gios==6.0.0
|
||||
gios==6.1.2
|
||||
|
||||
# homeassistant.components.glances
|
||||
glances-api==0.8.0
|
||||
@@ -982,13 +982,13 @@ ha-philipsjs==3.2.2
|
||||
ha-silabs-firmware-client==0.2.0
|
||||
|
||||
# homeassistant.components.habitica
|
||||
habiticalib==0.4.0
|
||||
habiticalib==0.4.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==3.49.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.104.0
|
||||
hass-nabucasa==0.106.0
|
||||
|
||||
# homeassistant.components.assist_satellite
|
||||
# homeassistant.components.conversation
|
||||
@@ -1017,13 +1017,13 @@ hole==0.8.0
|
||||
holidays==0.75
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20250702.0
|
||||
home-assistant-frontend==20250702.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2025.6.23
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==2.0.6
|
||||
homematicip==2.0.7
|
||||
|
||||
# homeassistant.components.remember_the_milk
|
||||
httplib2==0.20.4
|
||||
@@ -1244,7 +1244,7 @@ monzopy==1.4.2
|
||||
mopeka-iot-ble==0.8.0
|
||||
|
||||
# homeassistant.components.motion_blinds
|
||||
motionblinds==0.6.28
|
||||
motionblinds==0.6.29
|
||||
|
||||
# homeassistant.components.motionblinds_ble
|
||||
motionblindsble==0.1.3
|
||||
@@ -1259,7 +1259,7 @@ mozart-api==4.1.1.116.4
|
||||
mullvad-api==1.0.0
|
||||
|
||||
# homeassistant.components.music_assistant
|
||||
music-assistant-client==1.2.3
|
||||
music-assistant-client==1.2.4
|
||||
|
||||
# homeassistant.components.tts
|
||||
mutagen==1.47.0
|
||||
@@ -1329,7 +1329,7 @@ numato-gpio==0.13.0
|
||||
numpy==2.3.0
|
||||
|
||||
# homeassistant.components.nyt_games
|
||||
nyt_games==0.4.4
|
||||
nyt_games==0.5.0
|
||||
|
||||
# homeassistant.components.google
|
||||
oauth2client==4.1.3
|
||||
@@ -1637,7 +1637,7 @@ pyeiscp==0.0.7
|
||||
pyemoncms==0.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==2.1.0
|
||||
pyenphase==2.2.2
|
||||
|
||||
# homeassistant.components.everlights
|
||||
pyeverlights==0.1.0
|
||||
@@ -1745,7 +1745,7 @@ pykrakenapi==0.1.8
|
||||
pykulersky==0.5.8
|
||||
|
||||
# homeassistant.components.lamarzocco
|
||||
pylamarzocco==2.0.9
|
||||
pylamarzocco==2.0.11
|
||||
|
||||
# homeassistant.components.lastfm
|
||||
pylast==5.1.0
|
||||
@@ -1921,7 +1921,7 @@ pyrympro==0.0.9
|
||||
pysabnzbd==1.1.1
|
||||
|
||||
# homeassistant.components.schlage
|
||||
pyschlage==2025.4.0
|
||||
pyschlage==2025.7.2
|
||||
|
||||
# homeassistant.components.sensibo
|
||||
pysensibo==1.2.1
|
||||
@@ -1951,7 +1951,7 @@ pysmappee==0.2.29
|
||||
pysmarlaapi==0.9.0
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==3.2.5
|
||||
pysmartthings==3.2.8
|
||||
|
||||
# homeassistant.components.smarty
|
||||
pysmarty2==0.10.2
|
||||
@@ -1963,7 +1963,7 @@ pysmhi==1.0.2
|
||||
pysml==0.1.5
|
||||
|
||||
# homeassistant.components.smlight
|
||||
pysmlight==0.2.6
|
||||
pysmlight==0.2.7
|
||||
|
||||
# homeassistant.components.snmp
|
||||
pysnmp==6.2.6
|
||||
@@ -1987,7 +1987,7 @@ pysqueezebox==0.12.1
|
||||
pystiebeleltron==0.1.0
|
||||
|
||||
# homeassistant.components.suez_water
|
||||
pysuezV2==2.0.5
|
||||
pysuezV2==2.0.7
|
||||
|
||||
# homeassistant.components.switchbee
|
||||
pyswitchbee==1.8.3
|
||||
@@ -2154,7 +2154,7 @@ pywilight==0.0.74
|
||||
pywizlight==0.6.3
|
||||
|
||||
# homeassistant.components.wmspro
|
||||
pywmspro==0.3.0
|
||||
pywmspro==0.3.2
|
||||
|
||||
# homeassistant.components.ws66i
|
||||
pyws66i==1.1
|
||||
@@ -2278,7 +2278,7 @@ sentry-sdk==1.45.1
|
||||
sfrbox-api==0.0.11
|
||||
|
||||
# homeassistant.components.sharkiq
|
||||
sharkiq==1.1.0
|
||||
sharkiq==1.1.1
|
||||
|
||||
# homeassistant.components.simplefin
|
||||
simplefin4py==0.0.18
|
||||
@@ -2390,7 +2390,7 @@ temperusb==1.6.1
|
||||
# homeassistant.components.tesla_fleet
|
||||
# homeassistant.components.teslemetry
|
||||
# homeassistant.components.tessie
|
||||
tesla-fleet-api==1.2.0
|
||||
tesla-fleet-api==1.2.2
|
||||
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.5.2
|
||||
@@ -2468,7 +2468,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.14.1
|
||||
uiprotect==7.14.2
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
@@ -2509,7 +2509,7 @@ vehicle==2.2.2
|
||||
velbus-aio==2025.5.0
|
||||
|
||||
# homeassistant.components.venstar
|
||||
venstarcolortouch==0.19
|
||||
venstarcolortouch==0.21
|
||||
|
||||
# homeassistant.components.vilfo
|
||||
vilfo-api-client==0.5.0
|
||||
@@ -2634,7 +2634,7 @@ zeroconf==0.147.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.61
|
||||
zha==0.0.62
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.65.0
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user