Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1a772b6
Add button platform to LG Infrared (#166375)
abmantis Mar 25, 2026
8b69604
Improve select action naming consistency (#166398)
frenck Mar 25, 2026
bd40787
Bump ZHA to 1.1.0 (#166438)
TheJulianJES Mar 25, 2026
3dc478a
Filter out WiiM devices from LinkPlay discovery (#166436)
balloob Mar 25, 2026
b6508c2
Bump Tesla Fleet API to 1.4.5 (#166432)
Bre77 Mar 25, 2026
193f519
Warn about *.pth files in dependencies (#166411)
cdce8p Mar 25, 2026
6075bec
Improve siren action naming consistency (#166399)
frenck Mar 25, 2026
a3add17
Fix Tesla Fleet partner_login to not require vehicle scope. (#166435)
Bre77 Mar 25, 2026
a2b91a9
Fix `KeyError` for device temperature sensor in Unifi integration (#1…
bieniu Mar 25, 2026
91e9eb0
Fix Abode retrofit lock discovery (#166433)
goabodedevops Mar 25, 2026
f299b00
Add PARALLEL_UPDATES to Touchline SL climate platform (#166415)
jorgenvi Mar 25, 2026
171b8df
Add Presentation light to Liebherr (#166154)
mettolen Mar 25, 2026
8d91fd0
Improve lock action naming consistency (#166445)
frenck Mar 25, 2026
2367d7c
Proxmox add runtime entities (#166416)
erwindouna Mar 25, 2026
0af6a86
Improve reload action naming for YAML-based integrations (#166442)
frenck Mar 25, 2026
78e2514
Bump uiprotect to 10.2.3 (#166406)
RaHehl Mar 25, 2026
c055972
Get program from base program option at Home Connect (#164885)
Diegorro98 Mar 25, 2026
682eba9
Bump opower to 0.18.0 (#166444)
tronikos Mar 25, 2026
a6dd56e
Add custom equivalent units to recorder platform (#164893)
Sab44 Mar 25, 2026
32221a1
Fix open sockets in tests for Telegram bot (#166451)
hanwg Mar 25, 2026
d5ff890
Use Unix socket for Supervisor communication (#163907)
agners Mar 25, 2026
31b1270
Migrate touchline to has_entity_name = true (#166403)
joostlek Mar 25, 2026
5215e67
Add Binary Sensors to Casper Glow (#166130)
mikeodr Mar 25, 2026
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
2 changes: 1 addition & 1 deletion homeassistant/components/abode/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
},
"iot_class": "cloud_push",
"loggers": ["jaraco.abode", "lomond"],
"requirements": ["jaraco.abode==6.2.1"],
"requirements": ["jaraco.abode==6.4.0"],
"single_config_entry": true
}
12 changes: 7 additions & 5 deletions homeassistant/components/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def websocket_delete_all_refresh_tokens(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
"""Handle delete all refresh tokens request."""
current_refresh_token: RefreshToken
current_refresh_token: RefreshToken | None = None
remove_failed = False
token_type = msg.get("token_type")
delete_current_token = msg.get("delete_current_token")
Expand Down Expand Up @@ -654,7 +654,7 @@ def websocket_delete_all_refresh_tokens(
else:
connection.send_result(msg["id"], {})

async def _delete_current_token_soon() -> None:
async def _delete_current_token_soon(current_refresh_token: RefreshToken) -> None:
"""Delete the current token after a delay.
We do not want to delete the current token immediately as it will
Expand All @@ -675,13 +675,15 @@ async def _delete_current_token_soon() -> None:
# the token right away.
hass.auth.async_remove_refresh_token(current_refresh_token)

if delete_current_token and (
not limit_token_types or current_refresh_token.token_type == token_type
if (
delete_current_token
and current_refresh_token
and (not limit_token_types or current_refresh_token.token_type == token_type)
):
# Deleting the token will close the connection so we need
# to do it with a delay in a tracked task to ensure it still
# happens if Home Assistant is shutting down.
hass.async_create_task(_delete_current_token_soon())
hass.async_create_task(_delete_current_token_soon(current_refresh_token))


@websocket_api.websocket_command(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/casper_glow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from .coordinator import CasperGlowConfigEntry, CasperGlowCoordinator

PLATFORMS: list[Platform] = [Platform.LIGHT]
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.LIGHT]


async def async_setup_entry(hass: HomeAssistant, entry: CasperGlowConfigEntry) -> bool:
Expand Down
51 changes: 51 additions & 0 deletions homeassistant/components/casper_glow/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Casper Glow integration binary sensor platform."""

from __future__ import annotations

from pycasperglow import GlowState

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .coordinator import CasperGlowConfigEntry, CasperGlowCoordinator
from .entity import CasperGlowEntity

PARALLEL_UPDATES = 0


async def async_setup_entry(
hass: HomeAssistant,
entry: CasperGlowConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the binary sensor platform for Casper Glow."""
async_add_entities([CasperGlowPausedBinarySensor(entry.runtime_data)])


class CasperGlowPausedBinarySensor(CasperGlowEntity, BinarySensorEntity):
"""Binary sensor indicating whether the Casper Glow dimming is paused."""

_attr_translation_key = "paused"

def __init__(self, coordinator: CasperGlowCoordinator) -> None:
"""Initialize the paused binary sensor."""
super().__init__(coordinator)
self._attr_unique_id = f"{format_mac(coordinator.device.address)}_paused"
if coordinator.device.state.is_paused is not None:
self._attr_is_on = coordinator.device.state.is_paused

async def async_added_to_hass(self) -> None:
"""Register state update callback when entity is added."""
await super().async_added_to_hass()
self.async_on_remove(
self._device.register_callback(self._async_handle_state_update)
)

@callback
def _async_handle_state_update(self, state: GlowState) -> None:
"""Handle a state update from the device."""
if state.is_paused is not None:
self._attr_is_on = state.is_paused
self.async_write_ha_state()
9 changes: 9 additions & 0 deletions homeassistant/components/casper_glow/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"entity": {
"binary_sensor": {
"paused": {
"default": "mdi:timer-pause"
}
}
}
}
7 changes: 7 additions & 0 deletions homeassistant/components/casper_glow/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
}
}
},
"entity": {
"binary_sensor": {
"paused": {
"name": "Dimming paused"
}
}
},
"exceptions": {
"communication_error": {
"message": "An error occurred while communicating with the Casper Glow: {error}"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/derivative/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"services": {
"reload": {
"description": "Reloads derivative sensors from the YAML-configuration.",
"name": "[%key:common::action::reload%]"
"name": "Reload derivative sensors"
}
},
"title": "Derivative sensor"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/filter/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
"services": {
"reload": {
"description": "Reloads filters from the YAML-configuration.",
"name": "[%key:common::action::reload%]"
"name": "Reload filters"
}
},
"title": "Filter"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/generic_thermostat/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"services": {
"reload": {
"description": "Reloads generic thermostats from the YAML-configuration.",
"name": "[%key:common::action::reload%]"
"name": "Reload generic thermostats"
}
},
"title": "Generic thermostat"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/history_stats/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"services": {
"reload": {
"description": "Reloads history stats sensors from the YAML-configuration.",
"name": "[%key:common::action::reload%]"
"name": "Reload history stats sensors"
}
},
"title": "History Stats"
Expand Down
41 changes: 32 additions & 9 deletions homeassistant/components/home_connect/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ async def event_listener(self, event_message: EventMessage) -> None:
case EventType.NOTIFY:
settings = self.data.settings
events = self.data.events
program_update_event_value = None
for event in event_message.data.items:
event_key = event.key
if event_key in SettingKey.__members__.values(): # type: ignore[comparison-overlap]
Expand All @@ -330,11 +331,13 @@ async def event_listener(self, event_message: EventMessage) -> None:
EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM,
) and isinstance(event_value, str):
await self.update_options(
event_key,
ProgramKey(event_value),
)
program_update_event_value = ProgramKey(event_value)
events[event_key] = event
# Process program update after all events to ensure
# BSH_COMMON_OPTION_BASE_PROGRAM event is available for
# favorite program resolution
if program_update_event_value:
await self.update_options(program_update_event_value)
self._call_event_listener(event_message)

case EventType.EVENT:
Expand Down Expand Up @@ -493,7 +496,7 @@ async def get_appliance_data(self) -> None:
programs = []
events = {}
options = {}
if appliance.type in APPLIANCES_WITH_PROGRAMS:
if appliance.type in APPLIANCES_WITH_PROGRAMS: # pylint: disable=too-many-nested-blocks
try:
all_programs = await self.client.get_all_programs(appliance.ha_id)
except TooManyRequestsError:
Expand Down Expand Up @@ -529,6 +532,17 @@ async def get_appliance_data(self) -> None:
)
current_program_key = program.key
program_options = program.options
if (
current_program_key == ProgramKey.BSH_COMMON_FAVORITE_001
and program_options
):
# The API doesn't allow to fetch the options from the favorite program.
# We can attempt to get the base program and get the options
for option in program_options:
if option.key == OptionKey.BSH_COMMON_BASE_PROGRAM:
current_program_key = ProgramKey(option.value)
break

if current_program_key:
options = await self.get_options_definitions(current_program_key)
for option in program_options or []:
Expand Down Expand Up @@ -595,15 +609,24 @@ async def get_options_definitions(
)
return {}

async def update_options(
self, event_key: EventKey, program_key: ProgramKey
) -> None:
async def update_options(self, program_key: ProgramKey) -> None:
"""Update options for appliance."""
options = self.data.options
events = self.data.events
options_to_notify = options.copy()
options.clear()
options.update(await self.get_options_definitions(program_key))
if (
program_key == ProgramKey.BSH_COMMON_FAVORITE_001
and (event := events.get(EventKey.BSH_COMMON_OPTION_BASE_PROGRAM))
and isinstance(event.value, str)
):
# The API doesn't allow to fetch the options from the favorite program.
# We can attempt to get the base program and get the options
resolved_program_key = ProgramKey(event.value)
else:
resolved_program_key = program_key

options.update(await self.get_options_definitions(resolved_program_key))

for option in options.values():
option_value = option.constraints.default if option.constraints else None
Expand Down
17 changes: 15 additions & 2 deletions homeassistant/components/home_connect/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,24 @@ async def async_added_to_hass(self) -> None:
def update_native_value(self) -> None:
"""Set the program value."""
event = self.appliance.events.get(cast(EventKey, self.bsh_key))
self._attr_current_option = (
PROGRAMS_TRANSLATION_KEYS_MAP.get(ProgramKey(event_value))
program_key = (
ProgramKey(event_value)
if event and isinstance(event_value := event.value, str)
else None
)
if (
program_key == ProgramKey.BSH_COMMON_FAVORITE_001
and (
base_program_event := self.appliance.events.get(
EventKey.BSH_COMMON_OPTION_BASE_PROGRAM
)
)
and isinstance(base_program_event.value, str)
):
program_key = ProgramKey(base_program_event.value)
self._attr_current_option = (
PROGRAMS_TRANSLATION_KEYS_MAP.get(program_key) if program_key else None
)

async def async_select_option(self, option: str) -> None:
"""Select new program."""
Expand Down
Loading
Loading