Skip to content

[pull] dev from home-assistant:dev #641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
07e2cfb
Bump inkbird-ble to 0.15.0 (#143916)
bdraco Apr 30, 2025
653306e
Bump sensorpush-ble to 1.9.0 (#143917)
bdraco Apr 30, 2025
6236123
Bump thermobeacon-ble to 0.10.0 (#143918)
bdraco Apr 30, 2025
03b10b4
Bump sensorpro-ble to 0.7.0 (#143919)
bdraco Apr 30, 2025
2112b5a
Bump thermopro-ble to 0.13.0 (#143920)
bdraco Apr 30, 2025
f7240b5
Bump leaone-ble to 0.3.0 (#143921)
bdraco Apr 30, 2025
c3dac50
Bump bluemaestro-ble to 0.4.0 (#143922)
bdraco Apr 30, 2025
eabf88e
Fix Z-Wave USB discovery already configured (#143907)
MartinHjelmare Apr 30, 2025
34becb5
add `verify_ssl` config flow option to ntfy integration (#143731)
tr4nt0r Apr 30, 2025
dc02c37
Use snapshot_platform in renault tests (#143864)
epenet Apr 30, 2025
4ee3290
Improve ESPHome dashboard diagnostics (#143914)
bdraco Apr 30, 2025
40764b6
Cleanup renault test constants (#143924)
epenet Apr 30, 2025
c562cba
Use unique VIN in renault tests (#143925)
epenet Apr 30, 2025
4b6fa12
Make name a top-level key for SSDP discovery WebSocket API (#143923)
bdraco Apr 30, 2025
69c387a
Improve Renault plug status binary sensor (#143931)
tmenguy Apr 30, 2025
42d22bb
Use unique registration number in renault tests (#143926)
epenet Apr 30, 2025
09518b1
Remove redundant Renault test fixtures (#143929)
epenet Apr 30, 2025
98cbc2a
Add extra logging in samsungtv (#143933)
epenet Apr 30, 2025
04bea9c
Handle Z-Wave migration low SDK version (#143936)
MartinHjelmare Apr 30, 2025
a8bee20
Add Nuki brand with Matter support (#143904)
edenhaus Apr 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions homeassistant/brands/nuki.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"domain": "nuki",
"name": "Nuki",
"integrations": ["nuki"],
"iot_standards": ["matter"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/bluemaestro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bluemaestro",
"iot_class": "local_push",
"requirements": ["bluemaestro-ble==0.3.0"]
"requirements": ["bluemaestro-ble==0.4.0"]
}
25 changes: 24 additions & 1 deletion homeassistant/components/esphome/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
from homeassistant.core import HomeAssistant

from . import CONF_NOISE_PSK
from .const import CONF_DEVICE_NAME
from .dashboard import async_get_dashboard
from .entry_data import ESPHomeConfigEntry

REDACT_KEYS = {CONF_NOISE_PSK, CONF_PASSWORD, "mac_address", "bluetooth_mac_address"}
CONFIGURED_DEVICE_KEYS = (
"configuration",
"current_version",
"deployed_version",
"loaded_integrations",
"target_platform",
)


async def async_get_config_entry_diagnostics(
Expand All @@ -26,6 +34,9 @@ async def async_get_config_entry_diagnostics(

entry_data = config_entry.runtime_data
device_info = entry_data.device_info
device_name: str | None = (
device_info.name if device_info else config_entry.data.get(CONF_DEVICE_NAME)
)

if (storage_data := await entry_data.store.async_load()) is not None:
diag["storage_data"] = storage_data
Expand All @@ -45,7 +56,19 @@ async def async_get_config_entry_diagnostics(
"scanner": await scanner.async_diagnostics(),
}

diag_dashboard: dict[str, Any] = {"configured": False}
diag["dashboard"] = diag_dashboard
if dashboard := async_get_dashboard(hass):
diag["dashboard"] = dashboard.addon_slug
diag_dashboard["configured"] = True
diag_dashboard["supports_update"] = dashboard.supports_update
diag_dashboard["last_update_success"] = dashboard.last_update_success
diag_dashboard["last_exception"] = dashboard.last_exception
diag_dashboard["addon"] = dashboard.addon_slug
if device_name and dashboard.data:
diag_dashboard["has_matching_name"] = device_name in dashboard.data
if data := dashboard.data.get(device_name):
diag_dashboard["device"] = {
key: data.get(key) for key in CONFIGURED_DEVICE_KEYS
}

return async_redact_data(diag, REDACT_KEYS)
2 changes: 1 addition & 1 deletion homeassistant/components/inkbird/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/inkbird",
"iot_class": "local_push",
"requirements": ["inkbird-ble==0.14.1"]
"requirements": ["inkbird-ble==0.15.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/leaone/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/leaone",
"iot_class": "local_push",
"requirements": ["leaone-ble==0.2.0"]
"requirements": ["leaone-ble==0.3.0"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/ntfy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_TOKEN, CONF_URL, Platform
from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
Expand All @@ -30,7 +30,7 @@
async def async_setup_entry(hass: HomeAssistant, entry: NtfyConfigEntry) -> bool:
"""Set up ntfy from a config entry."""

session = async_get_clientsession(hass)
session = async_get_clientsession(hass, entry.data.get(CONF_VERIFY_SSL, True))
ntfy = Ntfy(entry.data[CONF_URL], session, token=entry.data.get(CONF_TOKEN))

try:
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/ntfy/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
CONF_TOKEN,
CONF_URL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
Expand All @@ -54,6 +55,7 @@
autocomplete="url",
),
),
vol.Required(CONF_VERIFY_SSL, default=True): bool,
vol.Required(SECTION_AUTH): data_entry_flow.section(
vol.Schema(
{
Expand Down Expand Up @@ -123,7 +125,7 @@ async def async_step_user(
CONF_USERNAME: username,
}
)
session = async_get_clientsession(self.hass)
session = async_get_clientsession(self.hass, user_input[CONF_VERIFY_SSL])
if username:
ntfy = Ntfy(
user_input[CONF_URL],
Expand Down Expand Up @@ -160,6 +162,7 @@ async def async_step_user(
CONF_URL: url.human_repr(),
CONF_USERNAME: username,
CONF_TOKEN: token,
CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL],
},
)

Expand Down
6 changes: 4 additions & 2 deletions homeassistant/components/ntfy/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"user": {
"description": "Set up **ntfy** push notification service",
"data": {
"url": "Service URL"
"url": "Service URL",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"url": "Address of the ntfy service. Modify this if you want to use a different server"
"url": "Address of the ntfy service. Modify this if you want to use a different server",
"verify_ssl": "Enable SSL certificate verification for secure connections. Disable only if connecting to a ntfy instance using a self-signed certificate"
},
"sections": {
"auth": {
Expand Down
40 changes: 36 additions & 4 deletions homeassistant/components/renault/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass

from renault_api.kamereon.enums import ChargeState, PlugState
Expand All @@ -22,6 +23,16 @@
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0

_PLUG_FROM_CHARGE_STATUS: set[ChargeState] = {
ChargeState.CHARGE_IN_PROGRESS,
ChargeState.WAITING_FOR_CURRENT_CHARGE,
ChargeState.CHARGE_ENDED,
ChargeState.V2G_CHARGING_NORMAL,
ChargeState.V2G_CHARGING_WAITING,
ChargeState.V2G_DISCHARGING,
ChargeState.WAITING_FOR_A_PLANNED_CHARGE,
}


@dataclass(frozen=True, kw_only=True)
class RenaultBinarySensorEntityDescription(
Expand All @@ -30,8 +41,9 @@ class RenaultBinarySensorEntityDescription(
):
"""Class describing Renault binary sensor entities."""

on_key: str
on_value: StateType
on_key: str | None = None
on_value: StateType | None = None
value_lambda: Callable[[RenaultBinarySensor], bool | None] | None = None


async def async_setup_entry(
Expand Down Expand Up @@ -59,20 +71,40 @@ class RenaultBinarySensor(
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""

if self.entity_description.value_lambda is not None:
return self.entity_description.value_lambda(self)
if self.entity_description.on_key is None:
raise NotImplementedError("Either value_lambda or on_key must be set")
if (data := self._get_data_attr(self.entity_description.on_key)) is None:
return None

return data == self.entity_description.on_value


def _plugged_in_value_lambda(self: RenaultBinarySensor) -> bool | None:
"""Return true if the vehicle is plugged in."""

data = self.coordinator.data
plug_status = data.get_plug_status() if data else None

if plug_status is not None:
return plug_status == PlugState.PLUGGED

charging_status = data.get_charging_status() if data else None
if charging_status is not None and charging_status in _PLUG_FROM_CHARGE_STATUS:
return True

return None


BINARY_SENSOR_TYPES: tuple[RenaultBinarySensorEntityDescription, ...] = tuple(
[
RenaultBinarySensorEntityDescription(
key="plugged_in",
coordinator="battery",
device_class=BinarySensorDeviceClass.PLUG,
on_key="plugStatus",
on_value=PlugState.PLUGGED.value,
value_lambda=_plugged_in_value_lambda,
),
RenaultBinarySensorEntityDescription(
key="charging",
Expand Down
14 changes: 11 additions & 3 deletions homeassistant/components/samsungtv/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,17 +566,25 @@ async def async_device_info(self, force: bool = False) -> dict[str, Any] | None:
"""Try to gather infos of this TV."""
if self._rest_api is None:
assert self.port
rest_api = SamsungTVAsyncRest(
self._rest_api = SamsungTVAsyncRest(
host=self.host,
session=async_get_clientsession(self.hass),
port=self.port,
timeout=TIMEOUT_WEBSOCKET,
)

with contextlib.suppress(*REST_EXCEPTIONS):
device_info: dict[str, Any] = await rest_api.rest_device_info()
try:
device_info: dict[str, Any] = await self._rest_api.rest_device_info()
LOGGER.debug("Device info on %s is: %s", self.host, device_info)
self._device_info = device_info
except REST_EXCEPTIONS as err:
LOGGER.debug(
"Failed to load device info from %s:%s: %s",
self.host,
self.port,
str(err),
)
else:
return device_info

return None if force else self._device_info
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/sensorpro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/sensorpro",
"iot_class": "local_push",
"requirements": ["sensorpro-ble==0.6.0"]
"requirements": ["sensorpro-ble==0.7.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/sensorpush/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/sensorpush",
"iot_class": "local_push",
"requirements": ["sensorpush-ble==1.8.0"]
"requirements": ["sensorpush-ble==1.9.0"]
}
13 changes: 11 additions & 2 deletions homeassistant/components/ssdp/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
from homeassistant.components import websocket_api
from homeassistant.core import HassJob, HomeAssistant, callback
from homeassistant.helpers.json import json_bytes
from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_FRIENDLY_NAME,
SsdpServiceInfo,
)

from .const import DOMAIN, SSDP_SCANNER
from .scanner import Scanner, SsdpChange
Expand Down Expand Up @@ -47,7 +50,13 @@ def _async_event_message(message: dict[str, Any]) -> None:
@callback
def _async_on_data(info: SsdpServiceInfo, change: SsdpChange) -> None:
if change is not SsdpChange.BYEBYE:
_async_event_message({"add": [asdict(info)]})
_async_event_message(
{
"add": [
{"name": info.upnp.get(ATTR_UPNP_FRIENDLY_NAME), **asdict(info)}
]
}
)
return
remove_msg = {
FIELD_SSDP_ST: info.ssdp_st,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/thermobeacon/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermobeacon",
"iot_class": "local_push",
"requirements": ["thermobeacon-ble==0.9.0"]
"requirements": ["thermobeacon-ble==0.10.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/thermopro/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermopro",
"iot_class": "local_push",
"requirements": ["thermopro-ble==0.12.0"]
"requirements": ["thermopro-ble==0.13.0"]
}
33 changes: 32 additions & 1 deletion homeassistant/components/zwave_js/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Any

import aiohttp
from awesomeversion import AwesomeVersion
from serial.tools import list_ports
import voluptuous as vol
from zwave_js_server.client import Client
Expand Down Expand Up @@ -99,6 +100,7 @@
}

ON_SUPERVISOR_SCHEMA = vol.Schema({vol.Optional(CONF_USE_ADDON, default=True): bool})
MIN_MIGRATION_SDK_VERSION = AwesomeVersion("6.61")


def get_manual_schema(user_input: dict[str, Any]) -> vol.Schema:
Expand Down Expand Up @@ -428,7 +430,15 @@ async def async_step_usb(self, discovery_info: UsbServiceInfo) -> ConfigFlowResu
"""Handle USB Discovery."""
if not is_hassio(self.hass):
return self.async_abort(reason="discovery_requires_supervisor")
if self._async_in_progress():
if any(
flow
for flow in self._async_in_progress()
if flow["context"].get("source") != SOURCE_USB
):
# Allow multiple USB discovery flows to be in progress.
# Migration requires more than one USB stick to be connected,
# which can cause more than one discovery flow to be in progress,
# at least for a short time.
return self.async_abort(reason="already_in_progress")
if current_config_entries := self._async_current_entries(include_ignore=False):
config_entry = next(
Expand Down Expand Up @@ -802,6 +812,27 @@ async def async_step_intent_migrate(
if not self._usb_discovery and not config_entry.data.get(CONF_USE_ADDON):
return self.async_abort(reason="addon_required")

try:
driver = self._get_driver()
except AbortFlow:
return self.async_abort(reason="config_entry_not_loaded")
if (
sdk_version := driver.controller.sdk_version
) is not None and sdk_version < MIN_MIGRATION_SDK_VERSION:
_LOGGER.warning(
"Migration from this controller that has SDK version %s "
"is not supported. If possible, update the firmware "
"of the controller to a firmware built using SDK version %s or higher",
sdk_version,
MIN_MIGRATION_SDK_VERSION,
)
return self.async_abort(
reason="migration_low_sdk_version",
description_placeholders={
"ok_sdk_version": str(MIN_MIGRATION_SDK_VERSION)
},
)

if user_input is not None:
self._migrating = True
return await self.async_step_backup_nvm()
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/zwave_js/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"backup_failed": "Failed to back up network.",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"config_entry_not_loaded": "The Z-Wave configuration entry is not loaded. Please try again when the configuration entry is loaded.",
"different_device": "The connected USB device is not the same as previously configured for this config entry. Please instead create a new config entry for the new device.",
"discovery_requires_supervisor": "Discovery requires the supervisor.",
"migration_low_sdk_version": "The SDK version of the old controller is lower than {ok_sdk_version}. This means it's not possible to migrate the Non Volatile Memory (NVM) of the old controller to another controller.\n\nCheck the documentation on the manufacturer support pages of the old controller, if it's possible to upgrade the firmware of the old controller to a version that is build with SDK version {ok_sdk_version} or higher.",
"migration_successful": "Migration successful.",
"not_zwave_device": "Discovered device is not a Z-Wave device.",
"not_zwave_js_addon": "Discovered add-on is not the official Z-Wave add-on.",
Expand Down
Loading