Skip to content
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
5f6b446
Migrate SleepIQ sensors to entity descriptions (#163213)
rhcp011235 Feb 18, 2026
723825b
Mark runtime-data quality as exempt in Splunk (#163359)
Bre77 Feb 18, 2026
122bc32
Add integration_type device to sensorpush (#163389)
joostlek Feb 18, 2026
2e0f727
Add integration_type hub to senz (#163391)
joostlek Feb 18, 2026
be25603
Refactor optimistic update and delayed refresh for Liebherr integrati…
mettolen Feb 18, 2026
ba547c6
Add channel muting switches to Onkyo (#162605)
arturpragacz Feb 18, 2026
6be1e40
Add Powerfox Local integration (#163302)
klaasnicolaas Feb 18, 2026
c727662
Add metadata validation for missing backup files in OneDrive backup a…
zweckj Feb 18, 2026
3b7b345
Simplify ecovacs unload and register teardown before initialize (#163…
edenhaus Feb 18, 2026
1fd8738
Bump aiostreammagic to 2.13.0 (#163408)
noahhusby Feb 18, 2026
8a1909e
Bump hass-splunk to 0.1.4 (#163413)
Bre77 Feb 18, 2026
14b147b
Mark Splunk dependency-transparency quality scale rule as done (#163355)
Bre77 Feb 18, 2026
0f874f7
Add Config Flow for Ness Alarm (#162414)
Poshy163 Feb 18, 2026
e9be363
add support for multi tariff meter data in iometer (#161767)
torben-iometer Feb 18, 2026
ca4d537
Control datetime on SwitchBot Meter Pro CO2 (#161808)
elgris Feb 18, 2026
fafa193
Add LED light support for WiredPushButton (HmIPW-WRC2/WRC6) (#161841)
lackas Feb 18, 2026
cd5775c
Add integration_type service to simplepush (#163394)
joostlek Feb 18, 2026
b398197
Debug logging for config_entries (#163378)
zweckj Feb 18, 2026
2fcbd77
Don't set last notification timestamp when sending message failed (#1…
tr4nt0r Feb 18, 2026
37f0f18
Add sleep health metrics to SleepIQ integration (#163403)
rhcp011235 Feb 19, 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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ homeassistant.components.plugwise.*
homeassistant.components.pooldose.*
homeassistant.components.portainer.*
homeassistant.components.powerfox.*
homeassistant.components.powerfox_local.*
homeassistant.components.powerwall.*
homeassistant.components.private_ble_device.*
homeassistant.components.prometheus.*
Expand Down
6 changes: 4 additions & 2 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions homeassistant/brands/powerfox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"domain": "powerfox",
"name": "Powerfox",
"integrations": ["powerfox", "powerfox_local"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/cambridge_audio/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"iot_class": "local_push",
"loggers": ["aiostreammagic"],
"quality_scale": "platinum",
"requirements": ["aiostreammagic==2.12.1"],
"requirements": ["aiostreammagic==2.13.0"],
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
}
7 changes: 3 additions & 4 deletions homeassistant/components/ecovacs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: EcovacsConfigEntry) -> bool:
"""Set up this integration using UI."""
controller = EcovacsController(hass, entry.data)
await controller.initialize()

async def on_unload() -> None:
await controller.teardown()
entry.async_on_unload(controller.teardown)

await controller.initialize()

entry.async_on_unload(on_unload)
entry.runtime_data = controller

async def _async_wait_connect(device: VacBot) -> None:
Expand Down
141 changes: 141 additions & 0 deletions homeassistant/components/homematicip_cloud/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
PluggableDimmer,
SwitchMeasuring,
WiredDimmer3,
WiredPushButton,
)
from packaging.version import Version

Expand Down Expand Up @@ -93,6 +94,20 @@ async def async_setup_entry(
(Dimmer, PluggableDimmer, BrandDimmer, FullFlushDimmer),
):
entities.append(HomematicipDimmer(hap, device))
elif isinstance(device, WiredPushButton):
optical_channels = sorted(
(
ch
for ch in device.functionalChannels
if ch.functionalChannelType
== FunctionalChannelType.OPTICAL_SIGNAL_CHANNEL
),
key=lambda ch: ch.index,
)
for led_number, ch in enumerate(optical_channels, start=1):
entities.append(
HomematicipOpticalSignalLight(hap, device, ch.index, led_number)
)

async_add_entities(entities)

Expand Down Expand Up @@ -421,3 +436,129 @@ def _convert_color(color: tuple) -> RGBColorState:
if 270 < hue <= 330:
return RGBColorState.PURPLE
return RGBColorState.RED


class HomematicipOpticalSignalLight(HomematicipGenericEntity, LightEntity):
"""Representation of HomematicIP WiredPushButton LED light."""

_attr_color_mode = ColorMode.HS
_attr_supported_color_modes = {ColorMode.HS}
_attr_supported_features = LightEntityFeature.EFFECT
_attr_translation_key = "optical_signal_light"

_effect_to_behaviour: dict[str, OpticalSignalBehaviour] = {
"on": OpticalSignalBehaviour.ON,
"blinking": OpticalSignalBehaviour.BLINKING_MIDDLE,
"flash": OpticalSignalBehaviour.FLASH_MIDDLE,
"billow": OpticalSignalBehaviour.BILLOW_MIDDLE,
}
_behaviour_to_effect: dict[OpticalSignalBehaviour, str] = {
v: k for k, v in _effect_to_behaviour.items()
}

_attr_effect_list = list(_effect_to_behaviour)

_color_switcher: dict[str, tuple[float, float]] = {
RGBColorState.WHITE: (0.0, 0.0),
RGBColorState.RED: (0.0, 100.0),
RGBColorState.YELLOW: (60.0, 100.0),
RGBColorState.GREEN: (120.0, 100.0),
RGBColorState.TURQUOISE: (180.0, 100.0),
RGBColorState.BLUE: (240.0, 100.0),
RGBColorState.PURPLE: (300.0, 100.0),
}

def __init__(
self,
hap: HomematicipHAP,
device: WiredPushButton,
channel_index: int,
led_number: int,
) -> None:
"""Initialize the optical signal light entity."""
super().__init__(
hap,
device,
post=f"LED {led_number}",
channel=channel_index,
is_multi_channel=True,
channel_real_index=channel_index,
)

@property
def is_on(self) -> bool:
"""Return true if light is on."""
channel = self.get_channel_or_raise()
return channel.on is True

@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
channel = self.get_channel_or_raise()
return int((channel.dimLevel or 0.0) * 255)

@property
def hs_color(self) -> tuple[float, float]:
"""Return the hue and saturation color value [float, float]."""
channel = self.get_channel_or_raise()
simple_rgb_color = channel.simpleRGBColorState
return self._color_switcher.get(simple_rgb_color, (0.0, 0.0))

@property
def effect(self) -> str | None:
"""Return the current effect."""
channel = self.get_channel_or_raise()
return self._behaviour_to_effect.get(channel.opticalSignalBehaviour)

@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the optical signal light."""
state_attr = super().extra_state_attributes
channel = self.get_channel_or_raise()

if self.is_on:
state_attr[ATTR_COLOR_NAME] = channel.simpleRGBColorState

return state_attr

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
# Use hs_color from kwargs, if not applicable use current hs_color.
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
simple_rgb_color = _convert_color(hs_color)

# If no kwargs, use default value.
brightness = 255
if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs[ATTR_BRIGHTNESS]

# Minimum brightness is 10, otherwise the LED is disabled
brightness = max(10, brightness)
dim_level = round(brightness / 255.0, 2)

effect = self.effect
if ATTR_EFFECT in kwargs:
effect = kwargs[ATTR_EFFECT]
elif effect is None:
effect = "on"

behaviour = self._effect_to_behaviour.get(effect, OpticalSignalBehaviour.ON)

await self._device.set_optical_signal_async(
channelIndex=self._channel,
opticalSignalBehaviour=behaviour,
rgb=simple_rgb_color,
dimLevel=dim_level,
)

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
channel = self.get_channel_or_raise()
simple_rgb_color = channel.simpleRGBColorState

await self._device.set_optical_signal_async(
channelIndex=self._channel,
opticalSignalBehaviour=OpticalSignalBehaviour.OFF,
rgb=simple_rgb_color,
dimLevel=0.0,
)
14 changes: 14 additions & 0 deletions homeassistant/components/homematicip_cloud/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@
}
},
"entity": {
"light": {
"optical_signal_light": {
"state_attributes": {
"effect": {
"state": {
"billow": "Billow",
"blinking": "Blinking",
"flash": "Flash",
"on": "[%key:common::state::on%]"
}
}
}
}
},
"sensor": {
"smoke_detector_alarm_counter": {
"name": "Alarm counter"
Expand Down
16 changes: 16 additions & 0 deletions homeassistant/components/iometer/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ class IOmeterEntityDescription(SensorEntityDescription):
options=["entered", "pending", "missing", "unknown"],
value_fn=lambda data: data.status.device.core.pin_status or STATE_UNKNOWN,
),
IOmeterEntityDescription(
key="consumption_tariff_t1",
translation_key="consumption_tariff_t1",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.reading.get_consumption_tariff_T1(),
),
IOmeterEntityDescription(
key="consumption_tariff_t2",
translation_key="consumption_tariff_t2",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.reading.get_consumption_tariff_T2(),
),
IOmeterEntityDescription(
key="total_consumption",
translation_key="total_consumption",
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/iometer/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
"battery_level": {
"name": "Battery level"
},
"consumption_tariff_t1": {
"name": "Consumption Tariff T1"
},
"consumption_tariff_t2": {
"name": "Consumption Tariff T2"
},
"core_bridge_rssi": {
"name": "Signal strength Core/Bridge"
},
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/liebherr/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Constants for the liebherr integration."""

from datetime import timedelta
from typing import Final

DOMAIN: Final = "liebherr"
MANUFACTURER: Final = "Liebherr"

REFRESH_DELAY: Final = timedelta(seconds=5)
32 changes: 29 additions & 3 deletions homeassistant/components/liebherr/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

from __future__ import annotations

from pyliebherrhomeapi import TemperatureControl, ZonePosition

import asyncio
from collections.abc import Coroutine
from typing import Any

from pyliebherrhomeapi import (
LiebherrConnectionError,
LiebherrTimeoutError,
TemperatureControl,
ZonePosition,
)

from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, MANUFACTURER
from .const import DOMAIN, MANUFACTURER, REFRESH_DELAY
from .coordinator import LiebherrCoordinator

# Zone position to translation key mapping
Expand Down Expand Up @@ -44,6 +54,22 @@ def __init__(
model_id=device.device_name,
)

async def _async_send_command(
self,
command: Coroutine[Any, Any, None],
) -> None:
"""Send a command with error handling and delayed refresh."""
try:
await command
except (LiebherrConnectionError, LiebherrTimeoutError) as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="communication_error",
) from err

await asyncio.sleep(REFRESH_DELAY.total_seconds())
await self.coordinator.async_request_refresh()


class LiebherrZoneEntity(LiebherrEntity):
"""Base entity for zone-based Liebherr entities.
Expand Down
Loading
Loading