Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e4f8d1a
Update frontend to 20260429.2 (#169748)
piitaya May 4, 2026
86415c1
OwnTracks: expose message tst as update_timestamp in device_tracker a…
kw6423 May 4, 2026
c22edbe
Add opening/closing state icons to valve domain (#169644)
cristoforocervino May 4, 2026
dfb8c7e
Fix uptime template sensor (#169743)
Petro31 May 4, 2026
664354c
Fix config flow validation in Nord Pool (#169751)
gjohansson-ST May 4, 2026
1140d52
Bump pytrydan to 1.0.0 (#169742)
dgomes May 4, 2026
5858db1
Use all_devices in ViCare diagnostics for completeness (#169429)
lackas May 4, 2026
2846dcc
Add delete service action to OneDrive integration (#168064)
leodrivera May 4, 2026
fcd2335
Add set_cover_position_and_tilt service to Overkiz (#169275)
optimusbasti May 4, 2026
6f28902
Refactor hassio coordinators to use typed dataclasses instead of dict…
mdegat01 May 4, 2026
2ed550c
Bump Insteon-panel to 0.6.2 (#169757)
ssyrell May 4, 2026
6319b3b
Raise repairs on platform setup for command_line (#153565)
gjohansson-ST May 4, 2026
1beeecd
Use SensorDeviceClass.UPTIME in WLED (#169708)
mik-laj May 4, 2026
6633f16
Add system health to Portainer (#169698)
erwindouna May 4, 2026
553ba5e
Add binary sensor to Nord Pool (#169684)
gjohansson-ST May 4, 2026
f90e9ce
Add Celsius and Fahrenheit to Smartthings UNITS mapping (#169686)
kernelpanic85 May 4, 2026
d0c0f02
Bump pyTibber to 0.37.3 (#169762)
Danielhiversen May 4, 2026
28d65e9
bump sense-energy to 0.14.1 (#169761)
kbickar May 4, 2026
8c8a863
Add ptdevices Integration (#156307)
frogman85978 May 4, 2026
1b4a7d5
Add precipitation device class to WeatherFlow Cloud accumulation sens…
shbatm May 4, 2026
63dfc97
Limit power status binary sensor to non-LR5 devices (#169659)
natekspencer May 4, 2026
57e66ba
Update Indevolt integration quality scale to silver (#167843)
Xirt May 4, 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 @@ -442,6 +442,7 @@ homeassistant.components.private_ble_device.*
homeassistant.components.prometheus.*
homeassistant.components.proximity.*
homeassistant.components.prusalink.*
homeassistant.components.ptdevices.*
homeassistant.components.pure_energie.*
homeassistant.components.purpleair.*
homeassistant.components.pushbullet.*
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.

7 changes: 6 additions & 1 deletion homeassistant/components/command_line/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import asyncio
from datetime import datetime, timedelta

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
)
from homeassistant.const import (
CONF_COMMAND,
CONF_NAME,
Expand All @@ -25,6 +28,7 @@

from .const import CONF_COMMAND_TIMEOUT, LOGGER, TRIGGER_ENTITY_OPTIONS
from .sensor import CommandSensorData
from .utils import create_platform_yaml_not_supported_issue

DEFAULT_NAME = "Binary Command Sensor"
DEFAULT_PAYLOAD_ON = "ON"
Expand All @@ -41,6 +45,7 @@ async def async_setup_platform(
) -> None:
"""Set up the Command line Binary Sensor."""
if not discovery_info:
create_platform_yaml_not_supported_issue(hass, BINARY_SENSOR_DOMAIN)
return

binary_sensor_config = discovery_info
Expand Down
9 changes: 7 additions & 2 deletions homeassistant/components/command_line/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any

from homeassistant.components.cover import CoverEntity
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN, CoverEntity
from homeassistant.const import (
CONF_COMMAND_CLOSE,
CONF_COMMAND_OPEN,
Expand All @@ -26,7 +26,11 @@
from homeassistant.util import dt as dt_util, slugify

from .const import CONF_COMMAND_TIMEOUT, LOGGER, TRIGGER_ENTITY_OPTIONS
from .utils import async_call_shell_with_timeout, async_check_output_or_log
from .utils import (
async_call_shell_with_timeout,
async_check_output_or_log,
create_platform_yaml_not_supported_issue,
)

SCAN_INTERVAL = timedelta(seconds=15)

Expand All @@ -39,6 +43,7 @@ async def async_setup_platform(
) -> None:
"""Set up cover controlled by shell commands."""
if not discovery_info:
create_platform_yaml_not_supported_issue(hass, COVER_DOMAIN)
return

covers = []
Expand Down
10 changes: 7 additions & 3 deletions homeassistant/components/command_line/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@
import subprocess
from typing import Any

from homeassistant.components.notify import BaseNotificationService
from homeassistant.components.notify import (
DOMAIN as NOTIFY_DOMAIN,
BaseNotificationService,
)
from homeassistant.const import CONF_COMMAND
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.process import kill_subprocess

from .const import CONF_COMMAND_TIMEOUT, LOGGER
from .utils import render_template_args
from .utils import create_platform_yaml_not_supported_issue, render_template_args

_LOGGER = logging.getLogger(__name__)


def get_service(
async def async_get_service(
hass: HomeAssistant,
config: ConfigType,
discovery_info: DiscoveryInfoType | None = None,
) -> CommandLineNotificationService | None:
"""Get the Command Line notification service."""
if not discovery_info:
create_platform_yaml_not_supported_issue(hass, NOTIFY_DOMAIN)
return None

notify_config = discovery_info
Expand Down
8 changes: 7 additions & 1 deletion homeassistant/components/command_line/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from jsonpath import jsonpath

from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import (
CONF_COMMAND,
CONF_NAME,
Expand All @@ -32,7 +33,11 @@
LOGGER,
TRIGGER_ENTITY_OPTIONS,
)
from .utils import async_check_output_or_log, render_template_args
from .utils import (
async_check_output_or_log,
create_platform_yaml_not_supported_issue,
render_template_args,
)

DEFAULT_NAME = "Command Sensor"

Expand All @@ -47,6 +52,7 @@ async def async_setup_platform(
) -> None:
"""Set up the Command Sensor."""
if not discovery_info:
create_platform_yaml_not_supported_issue(hass, SENSOR_DOMAIN)
return

sensor_config = discovery_info
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/command_line/strings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"issues": {
"platform_yaml_not_supported": {
"description": "Platform YAML setup is not supported.\nChange from configuring it using the `{platform}:` key to using the `command_line:` key directly in configuration.yaml and restart Home Assistant to resolve the issue.\nTo see the detailed documentation, select Learn more.",
"title": "Platform YAML is not supported in Command Line"
}
},
"services": {
"reload": {
"description": "Reloads command line configuration from the YAML-configuration.",
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/command_line/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any

from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchEntity
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
ENTITY_ID_FORMAT,
SwitchEntity,
)
from homeassistant.const import (
CONF_COMMAND_OFF,
CONF_COMMAND_ON,
Expand All @@ -25,7 +29,11 @@
from homeassistant.util import dt as dt_util, slugify

from .const import CONF_COMMAND_TIMEOUT, LOGGER, TRIGGER_ENTITY_OPTIONS
from .utils import async_call_shell_with_timeout, async_check_output_or_log
from .utils import (
async_call_shell_with_timeout,
async_check_output_or_log,
create_platform_yaml_not_supported_issue,
)

SCAN_INTERVAL = timedelta(seconds=30)

Expand All @@ -38,6 +46,7 @@ async def async_setup_platform(
) -> None:
"""Find and return switches controlled by shell commands."""
if not discovery_info:
create_platform_yaml_not_supported_issue(hass, SWITCH_DOMAIN)
return

switches = []
Expand Down
19 changes: 18 additions & 1 deletion homeassistant/components/command_line/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from homeassistant.core import HomeAssistant
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.template import Template

from .const import LOGGER
from .const import DOMAIN, LOGGER

_EXEC_FAILED_CODE = 127

Expand Down Expand Up @@ -91,3 +92,19 @@ def render_template_args(hass: HomeAssistant, command: str) -> str | None:
LOGGER.debug("Running command: %s", command)

return command


def create_platform_yaml_not_supported_issue(
hass: HomeAssistant, platform_domain: str
) -> None:
"""Create an issue when platform yaml is used."""
async_create_issue(
hass,
DOMAIN,
f"{platform_domain}_platform_yaml_not_supported",
is_fixable=False,
severity=IssueSeverity.ERROR,
translation_key="platform_yaml_not_supported",
translation_placeholders={"platform": platform_domain},
learn_more_url="https://www.home-assistant.io/integrations/command_line/",
)
2 changes: 1 addition & 1 deletion homeassistant/components/emulated_kasa/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.14.0"]
"requirements": ["sense-energy==0.14.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
"integration_type": "system",
"preview_features": { "winter_mode": {} },
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20260429.1"]
"requirements": ["home-assistant-frontend==20260429.2"]
}
75 changes: 35 additions & 40 deletions homeassistant/components/hassio/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Binary sensor platform for Hass.io addons."""

from collections.abc import Callable
from dataclasses import dataclass
import itertools

from aiohasupervisor.models import AddonState
from aiohasupervisor.models.mounts import MountState

from homeassistant.components.binary_sensor import (
Expand All @@ -14,41 +15,46 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import (
ADDONS_COORDINATOR,
ATTR_STARTED,
ATTR_STATE,
DATA_KEY_ADDONS,
DATA_KEY_MOUNTS,
MAIN_COORDINATOR,
)
from .const import ADDONS_COORDINATOR, MAIN_COORDINATOR
from .entity import HassioAddonEntity, HassioMountEntity


@dataclass(frozen=True)
class HassioBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Hassio binary sensor entity description."""
@dataclass(frozen=True, kw_only=True)
class HassioAddonBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Hass.io add-on binary sensor entity description."""

value_fn: Callable[[HassioAddonBinarySensor], bool]


target: str | None = None
@dataclass(frozen=True, kw_only=True)
class HassioMountBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Hass.io mount binary sensor entity description."""

value_fn: Callable[[HassioMountBinarySensor], bool]


ADDON_ENTITY_DESCRIPTIONS = (
HassioBinarySensorEntityDescription(
HassioAddonBinarySensorEntityDescription(
device_class=BinarySensorDeviceClass.RUNNING,
entity_registry_enabled_default=False,
key=ATTR_STATE,
key="state",
translation_key="state",
target=ATTR_STARTED,
value_fn=lambda entity: (
entity.coordinator.data.addons[entity.addon_slug].addon.state
== AddonState.STARTED
),
),
)

MOUNT_ENTITY_DESCRIPTIONS = (
HassioBinarySensorEntityDescription(
HassioMountBinarySensorEntityDescription(
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_registry_enabled_default=False,
key=ATTR_STATE,
key="state",
translation_key="mount",
target=MountState.ACTIVE.value,
value_fn=lambda entity: (
entity.coordinator.data.mounts[entity.mount_name].state == MountState.ACTIVE
),
),
)

Expand All @@ -63,57 +69,46 @@ async def async_setup_entry(
coordinator = hass.data[MAIN_COORDINATOR]

async_add_entities(
itertools.chain(
[
[
*[
HassioAddonBinarySensor(
addon=addon,
coordinator=addons_coordinator,
entity_description=entity_description,
)
for addon in addons_coordinator.data[DATA_KEY_ADDONS].values()
for addon in addons_coordinator.data.addons.values()
for entity_description in ADDON_ENTITY_DESCRIPTIONS
],
[
*[
HassioMountBinarySensor(
mount=mount,
coordinator=coordinator,
entity_description=entity_description,
)
for mount in coordinator.data[DATA_KEY_MOUNTS].values()
for mount in coordinator.data.mounts.values()
for entity_description in MOUNT_ENTITY_DESCRIPTIONS
],
)
]
)


class HassioAddonBinarySensor(HassioAddonEntity, BinarySensorEntity):
"""Binary sensor for Hass.io add-ons."""

entity_description: HassioBinarySensorEntityDescription
entity_description: HassioAddonBinarySensorEntityDescription

@property
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
value = self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][
self.entity_description.key
]
if self.entity_description.target is None:
return value
return value == self.entity_description.target
return self.entity_description.value_fn(self)


class HassioMountBinarySensor(HassioMountEntity, BinarySensorEntity):
"""Binary sensor for Hass.io mount."""

entity_description: HassioBinarySensorEntityDescription
entity_description: HassioMountBinarySensorEntityDescription

@property
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
value = getattr(
self.coordinator.data[DATA_KEY_MOUNTS][self._mount.name],
self.entity_description.key,
)
if self.entity_description.target is None:
return value
return value == self.entity_description.target
return self.entity_description.value_fn(self)
Loading
Loading