Merge branch 'dev' into epenet-20250602-1736

This commit is contained in:
epenet 2025-06-03 10:19:47 +02:00 committed by GitHub
commit ac367bc343
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 201 additions and 207 deletions

View File

@ -360,7 +360,7 @@ jobs:
- name: Run ruff - name: Run ruff
run: | run: |
. venv/bin/activate . venv/bin/activate
pre-commit run --hook-stage manual ruff --all-files --show-diff-on-failure pre-commit run --hook-stage manual ruff-check --all-files --show-diff-on-failure
env: env:
RUFF_OUTPUT_FORMAT: github RUFF_OUTPUT_FORMAT: github

View File

@ -1,8 +1,8 @@
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.0 rev: v0.11.12
hooks: hooks:
- id: ruff - id: ruff-check
args: args:
- --fix - --fix
- id: ruff-format - id: ruff-format
@ -30,7 +30,7 @@ repos:
- --branch=master - --branch=master
- --branch=rc - --branch=rc
- repo: https://github.com/adrienverge/yamllint.git - repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1 rev: v1.37.1
hooks: hooks:
- id: yamllint - id: yamllint
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier

2
.vscode/tasks.json vendored
View File

@ -45,7 +45,7 @@
{ {
"label": "Ruff", "label": "Ruff",
"type": "shell", "type": "shell",
"command": "pre-commit run ruff --all-files", "command": "pre-commit run ruff-check --all-files",
"group": { "group": {
"kind": "test", "kind": "test",
"isDefault": true "isDefault": true

View File

@ -118,5 +118,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aioamazondevices"], "loggers": ["aioamazondevices"],
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["aioamazondevices==3.0.4"] "requirements": ["aioamazondevices==3.0.5"]
} }

View File

@ -8,6 +8,6 @@
"integration_type": "system", "integration_type": "system",
"iot_class": "local_polling", "iot_class": "local_polling",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["go2rtc-client==0.1.3b0"], "requirements": ["go2rtc-client==0.2.1"],
"single_config_entry": true "single_config_entry": true
} }

View File

@ -24,9 +24,11 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Google Mail platform.""" """Set up the Google Mail integration."""
hass.data.setdefault(DOMAIN, {})[DATA_HASS_CONFIG] = config hass.data.setdefault(DOMAIN, {})[DATA_HASS_CONFIG] = config
await async_setup_services(hass)
return True return True
@ -52,8 +54,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoogleMailConfigEntry) -
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY] entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
) )
await async_setup_services(hass)
return True return True

View File

@ -7,17 +7,26 @@ from google_photos_library_api.api import GooglePhotosLibraryApi
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from . import api from . import api
from .const import DOMAIN from .const import DOMAIN
from .coordinator import GooglePhotosConfigEntry, GooglePhotosUpdateCoordinator from .coordinator import GooglePhotosConfigEntry, GooglePhotosUpdateCoordinator
from .services import async_register_services from .services import async_register_services
__all__ = [ __all__ = ["DOMAIN"]
"DOMAIN",
] CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Google Photos integration."""
async_register_services(hass)
return True
async def async_setup_entry( async def async_setup_entry(
@ -48,8 +57,6 @@ async def async_setup_entry(
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator entry.runtime_data = coordinator
async_register_services(hass)
return True return True

View File

@ -152,11 +152,10 @@ def async_register_services(hass: HomeAssistant) -> None:
} }
return None return None
if not hass.services.has_service(DOMAIN, UPLOAD_SERVICE): hass.services.async_register(
hass.services.async_register( DOMAIN,
DOMAIN, UPLOAD_SERVICE,
UPLOAD_SERVICE, async_handle_upload,
async_handle_upload, schema=UPLOAD_SERVICE_SCHEMA,
schema=UPLOAD_SERVICE_SCHEMA, supports_response=SupportsResponse.OPTIONAL,
supports_response=SupportsResponse.OPTIONAL, )
)

View File

@ -14,6 +14,6 @@
"documentation": "https://www.home-assistant.io/integrations/homekit_controller", "documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["aiohomekit", "commentjson"], "loggers": ["aiohomekit", "commentjson"],
"requirements": ["aiohomekit==3.2.14"], "requirements": ["aiohomekit==3.2.15"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
} }

View File

@ -63,6 +63,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
) )
) )
await async_setup_services(hass)
return True return True
@ -83,7 +85,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomematicIPConfigEntry)
if not await hap.async_setup(): if not await hap.async_setup():
return False return False
await async_setup_services(hass)
_async_remove_obsolete_entities(hass, entry, hap) _async_remove_obsolete_entities(hass, entry, hap)
# Register on HA stop event to gracefully shutdown HomematicIP Cloud connection # Register on HA stop event to gracefully shutdown HomematicIP Cloud connection

View File

@ -12,6 +12,6 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["homewizard_energy"], "loggers": ["homewizard_energy"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["python-homewizard-energy==v8.3.2"], "requirements": ["python-homewizard-energy==8.3.3"],
"zeroconf": ["_hwenergy._tcp.local.", "_homewizard._tcp.local."] "zeroconf": ["_hwenergy._tcp.local.", "_homewizard._tcp.local."]
} }

View File

@ -5,13 +5,24 @@ from aiohue.util import normalize_bridge_id
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.config_entries import SOURCE_IGNORE from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.typing import ConfigType
from .bridge import HueBridge, HueConfigEntry from .bridge import HueBridge, HueConfigEntry
from .const import DOMAIN, SERVICE_HUE_ACTIVATE_SCENE from .const import DOMAIN
from .migration import check_migration from .migration import check_migration
from .services import async_register_services from .services import async_register_services
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Hue integration."""
async_register_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Set up a bridge from a config entry.""" """Set up a bridge from a config entry."""
@ -23,9 +34,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
if not await bridge.async_initialize_bridge(): if not await bridge.async_initialize_bridge():
return False return False
# register Hue domain services
async_register_services(hass)
api = bridge.api api = bridge.api
# For backwards compat # For backwards compat
@ -106,7 +114,4 @@ async def async_setup_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: HueConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
unload_success = await entry.runtime_data.async_reset() return await entry.runtime_data.async_reset()
if not hass.config_entries.async_loaded_entries(DOMAIN):
hass.services.async_remove(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE)
return unload_success

View File

@ -59,21 +59,20 @@ def async_register_services(hass: HomeAssistant) -> None:
group_name, group_name,
) )
if not hass.services.has_service(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE): # Register a local handler for scene activation
# Register a local handler for scene activation hass.services.async_register(
hass.services.async_register( DOMAIN,
DOMAIN, SERVICE_HUE_ACTIVATE_SCENE,
SERVICE_HUE_ACTIVATE_SCENE, verify_domain_control(hass, DOMAIN)(hue_activate_scene),
verify_domain_entity_control(DOMAIN)(hue_activate_scene), schema=vol.Schema(
schema=vol.Schema( {
{ vol.Required(ATTR_GROUP_NAME): cv.string,
vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string,
vol.Required(ATTR_SCENE_NAME): cv.string, vol.Optional(ATTR_TRANSITION): cv.positive_int,
vol.Optional(ATTR_TRANSITION): cv.positive_int, vol.Optional(ATTR_DYNAMIC): cv.boolean,
vol.Optional(ATTR_DYNAMIC): cv.boolean, }
} ),
), )
)
async def hue_activate_scene_v1( async def hue_activate_scene_v1(

View File

@ -153,49 +153,40 @@ class ImmichMediaSource(MediaSource):
except ImmichError: except ImmichError:
return [] return []
ret = [ ret: list[BrowseMediaSource] = []
BrowseMediaSource( for asset in album_info.assets:
domain=DOMAIN, if not (mime_type := asset.original_mime_type) or not mime_type.startswith(
identifier=( ("image/", "video/")
f"{identifier.unique_id}|albums|" ):
f"{identifier.collection_id}|" continue
f"{asset.asset_id}|"
f"{asset.original_file_name}|"
f"{mime_type}"
),
media_class=MediaClass.IMAGE,
media_content_type=mime_type,
title=asset.original_file_name,
can_play=False,
can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/{mime_type}",
)
for asset in album_info.assets
if (mime_type := asset.original_mime_type)
and mime_type.startswith("image/")
]
ret.extend( if mime_type.startswith("image/"):
BrowseMediaSource( media_class = MediaClass.IMAGE
domain=DOMAIN, can_play = False
identifier=( thumb_mime_type = mime_type
f"{identifier.unique_id}|albums|" else:
f"{identifier.collection_id}|" media_class = MediaClass.VIDEO
f"{asset.asset_id}|" can_play = True
f"{asset.original_file_name}|" thumb_mime_type = "image/jpeg"
f"{mime_type}"
), ret.append(
media_class=MediaClass.VIDEO, BrowseMediaSource(
media_content_type=mime_type, domain=DOMAIN,
title=asset.original_file_name, identifier=(
can_play=True, f"{identifier.unique_id}|albums|"
can_expand=False, f"{identifier.collection_id}|"
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/image/jpeg", f"{asset.asset_id}|"
f"{asset.original_file_name}|"
f"{mime_type}"
),
media_class=media_class,
media_content_type=mime_type,
title=asset.original_file_name,
can_play=can_play,
can_expand=False,
thumbnail=f"/immich/{identifier.unique_id}/{asset.asset_id}/thumbnail/{thumb_mime_type}",
)
) )
for asset in album_info.assets
if (mime_type := asset.original_mime_type)
and mime_type.startswith("video/")
)
return ret return ret

View File

@ -26,6 +26,7 @@ from homeassistant.helpers import (
device_registry as dr, device_registry as dr,
) )
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
_LOGGER, _LOGGER,
@ -46,7 +47,7 @@ from .const import (
) )
from .helpers import _categorize_nodes, _categorize_programs from .helpers import _categorize_nodes, _categorize_programs
from .models import IsyConfigEntry, IsyData from .models import IsyConfigEntry, IsyData
from .services import async_setup_services, async_unload_services from .services import async_setup_services
from .util import _async_cleanup_registry_entries from .util import _async_cleanup_registry_entries
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
@ -55,6 +56,14 @@ CONFIG_SCHEMA = vol.Schema(
) )
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the ISY 994 integration."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
"""Set up the ISY 994 integration.""" """Set up the ISY 994 integration."""
isy_config = entry.data isy_config = entry.data
@ -167,9 +176,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_auto_update) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_auto_update)
) )
# Register Integration-wide Services:
async_setup_services(hass)
return True return True
@ -221,9 +227,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool
_LOGGER.debug("ISY Stopping Event Stream and automatic updates") _LOGGER.debug("ISY Stopping Event Stream and automatic updates")
entry.runtime_data.root.websocket.stop() entry.runtime_data.root.websocket.stop()
if not hass.config_entries.async_loaded_entries(DOMAIN):
async_unload_services(hass)
return unload_ok return unload_ok

View File

@ -137,10 +137,6 @@ def async_get_entities(hass: HomeAssistant) -> dict[str, Entity]:
@callback @callback
def async_setup_services(hass: HomeAssistant) -> None: def async_setup_services(hass: HomeAssistant) -> None:
"""Create and register services for the ISY integration.""" """Create and register services for the ISY integration."""
existing_services = hass.services.async_services_for_domain(DOMAIN)
if existing_services and SERVICE_SEND_PROGRAM_COMMAND in existing_services:
# Integration-level services have already been added. Return.
return
async def async_send_program_command_service_handler(service: ServiceCall) -> None: async def async_send_program_command_service_handler(service: ServiceCall) -> None:
"""Handle a send program command service call.""" """Handle a send program command service call."""
@ -230,18 +226,3 @@ def async_setup_services(hass: HomeAssistant) -> None:
schema=cv.make_entity_service_schema(SERVICE_RENAME_NODE_SCHEMA), schema=cv.make_entity_service_schema(SERVICE_RENAME_NODE_SCHEMA),
service_func=_async_rename_node, service_func=_async_rename_node,
) )
@callback
def async_unload_services(hass: HomeAssistant) -> None:
"""Unload services for the ISY integration."""
existing_services = hass.services.async_services_for_domain(DOMAIN)
if not existing_services or SERVICE_SEND_PROGRAM_COMMAND not in existing_services:
return
_LOGGER.debug("Unloading ISY994 Services")
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_PROGRAM_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_RAW_NODE_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SEND_NODE_COMMAND)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_GET_ZWAVE_PARAMETER)
hass.services.async_remove(domain=DOMAIN, service=SERVICE_SET_ZWAVE_PARAMETER)

View File

@ -30,7 +30,7 @@ from .const import (
DOMAIN, DOMAIN,
) )
from .entity import JewishCalendarConfigEntry, JewishCalendarData from .entity import JewishCalendarConfigEntry, JewishCalendarData
from .service import async_setup_services from .services import async_setup_services
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]

View File

@ -12,5 +12,5 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["pyatmo"], "loggers": ["pyatmo"],
"requirements": ["pyatmo==9.2.0"] "requirements": ["pyatmo==9.2.1"]
} }

View File

@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/ollama", "documentation": "https://www.home-assistant.io/integrations/ollama",
"integration_type": "service", "integration_type": "service",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["ollama==0.4.7"] "requirements": ["ollama==0.5.1"]
} }

View File

@ -121,11 +121,10 @@ def async_register_services(hass: HomeAssistant) -> None:
return {"files": [asdict(item_result) for item_result in upload_results]} return {"files": [asdict(item_result) for item_result in upload_results]}
return None return None
if not hass.services.has_service(DOMAIN, UPLOAD_SERVICE): hass.services.async_register(
hass.services.async_register( DOMAIN,
DOMAIN, UPLOAD_SERVICE,
UPLOAD_SERVICE, async_handle_upload,
async_handle_upload, schema=UPLOAD_SERVICE_SCHEMA,
schema=UPLOAD_SERVICE_SCHEMA, supports_response=SupportsResponse.OPTIONAL,
supports_response=SupportsResponse.OPTIONAL, )
)

View File

@ -13,7 +13,7 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"], "loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"],
"requirements": ["pyoverkiz==1.17.1"], "requirements": ["pyoverkiz==1.17.2"],
"zeroconf": [ "zeroconf": [
{ {
"type": "_kizbox._tcp.local.", "type": "_kizbox._tcp.local.",

View File

@ -5,14 +5,25 @@ from python_picnic_api2 import PicnicAPI
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_COUNTRY_CODE, Platform from homeassistant.const import CONF_ACCESS_TOKEN, CONF_COUNTRY_CODE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import CONF_API, CONF_COORDINATOR, DOMAIN from .const import CONF_API, CONF_COORDINATOR, DOMAIN
from .coordinator import PicnicUpdateCoordinator from .coordinator import PicnicUpdateCoordinator
from .services import async_register_services from .services import async_register_services
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
PLATFORMS = [Platform.SENSOR, Platform.TODO] PLATFORMS = [Platform.SENSOR, Platform.TODO]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Picnic integration."""
await async_register_services(hass)
return True
def create_picnic_client(entry: ConfigEntry): def create_picnic_client(entry: ConfigEntry):
"""Create an instance of the PicnicAPI client.""" """Create an instance of the PicnicAPI client."""
return PicnicAPI( return PicnicAPI(
@ -37,9 +48,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Register the services
await async_register_services(hass)
return True return True

View File

@ -29,9 +29,6 @@ class PicnicServiceException(Exception):
async def async_register_services(hass: HomeAssistant) -> None: async def async_register_services(hass: HomeAssistant) -> None:
"""Register services for the Picnic integration, if not registered yet.""" """Register services for the Picnic integration, if not registered yet."""
if hass.services.has_service(DOMAIN, SERVICE_ADD_PRODUCT_TO_CART):
return
async def async_add_product_service(call: ServiceCall): async def async_add_product_service(call: ServiceCall):
api_client = await get_api_client(hass, call.data[ATTR_CONFIG_ENTRY_ID]) api_client = await get_api_client(hass, call.data[ATTR_CONFIG_ENTRY_ID])
await handle_add_product(hass, api_client, call) await handle_add_product(hass, api_client, call)

View File

@ -13,5 +13,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aiokem"], "loggers": ["aiokem"],
"quality_scale": "silver", "quality_scale": "silver",
"requirements": ["aiokem==0.5.12"] "requirements": ["aiokem==1.0.1"]
} }

View File

@ -7,7 +7,7 @@ from dataclasses import dataclass
from functools import partial from functools import partial
from typing import TYPE_CHECKING, Any, Final from typing import TYPE_CHECKING, Any, Final
from aioshelly.const import BLU_TRV_IDENTIFIER, MODEL_BLU_GATEWAY, RPC_GENERATIONS from aioshelly.const import BLU_TRV_IDENTIFIER, MODEL_BLU_GATEWAY_G3, RPC_GENERATIONS
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
from homeassistant.components.button import ( from homeassistant.components.button import (
@ -62,7 +62,7 @@ BUTTONS: Final[list[ShellyButtonDescription[Any]]] = [
translation_key="self_test", translation_key="self_test",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
press_action="trigger_shelly_gas_self_test", press_action="trigger_shelly_gas_self_test",
supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, supported=lambda coordinator: coordinator.model in SHELLY_GAS_MODELS,
), ),
ShellyButtonDescription[ShellyBlockCoordinator]( ShellyButtonDescription[ShellyBlockCoordinator](
key="mute", key="mute",
@ -70,7 +70,7 @@ BUTTONS: Final[list[ShellyButtonDescription[Any]]] = [
translation_key="mute", translation_key="mute",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
press_action="trigger_shelly_gas_mute", press_action="trigger_shelly_gas_mute",
supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, supported=lambda coordinator: coordinator.model in SHELLY_GAS_MODELS,
), ),
ShellyButtonDescription[ShellyBlockCoordinator]( ShellyButtonDescription[ShellyBlockCoordinator](
key="unmute", key="unmute",
@ -78,7 +78,7 @@ BUTTONS: Final[list[ShellyButtonDescription[Any]]] = [
translation_key="unmute", translation_key="unmute",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
press_action="trigger_shelly_gas_unmute", press_action="trigger_shelly_gas_unmute",
supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS, supported=lambda coordinator: coordinator.model in SHELLY_GAS_MODELS,
), ),
] ]
@ -89,7 +89,7 @@ BLU_TRV_BUTTONS: Final[list[ShellyButtonDescription]] = [
translation_key="calibrate", translation_key="calibrate",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
press_action="trigger_blu_trv_calibration", press_action="trigger_blu_trv_calibration",
supported=lambda coordinator: coordinator.device.model == MODEL_BLU_GATEWAY, supported=lambda coordinator: coordinator.model == MODEL_BLU_GATEWAY_G3,
), ),
] ]
@ -160,6 +160,7 @@ async def async_setup_entry(
ShellyBluTrvButton(coordinator, button, id_) ShellyBluTrvButton(coordinator, button, id_)
for id_ in blutrv_key_ids for id_ in blutrv_key_ids
for button in BLU_TRV_BUTTONS for button in BLU_TRV_BUTTONS
if button.supported(coordinator)
) )
async_add_entities(entities) async_add_entities(entities)

View File

@ -12,7 +12,8 @@ from synology_dsm.exceptions import SynologyDSMNotLoggedInException
from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.typing import ConfigType
from .common import SynoApi, raise_config_entry_auth_error from .common import SynoApi, raise_config_entry_auth_error
from .const import ( from .const import (
@ -34,10 +35,20 @@ from .coordinator import (
SynologyDSMData, SynologyDSMData,
SynologyDSMSwitchUpdateCoordinator, SynologyDSMSwitchUpdateCoordinator,
) )
from .service import async_setup_services from .services import async_setup_services
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Synology DSM component."""
await async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: SynologyDSMConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: SynologyDSMConfigEntry) -> bool:
"""Set up Synology DSM sensors.""" """Set up Synology DSM sensors."""
@ -89,9 +100,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: SynologyDSMConfigEntry)
details = EXCEPTION_UNKNOWN details = EXCEPTION_UNKNOWN
raise ConfigEntryNotReady(details) from err raise ConfigEntryNotReady(details) from err
# Services
await async_setup_services(hass)
# For SSDP compat # For SSDP compat
if not entry.data.get(CONF_MAC): if not entry.data.get(CONF_MAC):
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(

View File

@ -6,7 +6,7 @@ aiodns==3.4.0
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
aiohttp-asyncmdnsresolver==0.1.1 aiohttp-asyncmdnsresolver==0.1.1
aiohttp-fast-zlib==0.2.3 aiohttp-fast-zlib==0.2.3
aiohttp==3.12.6 aiohttp==3.12.7
aiohttp_cors==0.8.1 aiohttp_cors==0.8.1
aiousbwatcher==1.1.1 aiousbwatcher==1.1.1
aiozoneinfo==0.2.3 aiozoneinfo==0.2.3
@ -32,7 +32,7 @@ cronsim==2.6
cryptography==45.0.3 cryptography==45.0.3
dbus-fast==2.43.0 dbus-fast==2.43.0
fnv-hash-fast==1.5.0 fnv-hash-fast==1.5.0
go2rtc-client==0.1.3b0 go2rtc-client==0.2.1
ha-ffmpeg==3.2.2 ha-ffmpeg==3.2.2
habluetooth==3.48.2 habluetooth==3.48.2
hass-nabucasa==0.101.0 hass-nabucasa==0.101.0
@ -66,7 +66,7 @@ securetar==2025.2.1
SQLAlchemy==2.0.41 SQLAlchemy==2.0.41
standard-aifc==3.13.0 standard-aifc==3.13.0
standard-telnetlib==3.13.0 standard-telnetlib==3.13.0
typing-extensions>=4.13.0,<5.0 typing-extensions>=4.14.0,<5.0
ulid-transform==1.4.0 ulid-transform==1.4.0
urllib3>=1.26.5,<2 urllib3>=1.26.5,<2
uv==0.7.1 uv==0.7.1

View File

@ -28,7 +28,7 @@ dependencies = [
# change behavior based on presence of supervisor. Deprecated with #127228 # change behavior based on presence of supervisor. Deprecated with #127228
# Lib can be removed with 2025.11 # Lib can be removed with 2025.11
"aiohasupervisor==0.3.1", "aiohasupervisor==0.3.1",
"aiohttp==3.12.6", "aiohttp==3.12.7",
"aiohttp_cors==0.8.1", "aiohttp_cors==0.8.1",
"aiohttp-fast-zlib==0.2.3", "aiohttp-fast-zlib==0.2.3",
"aiohttp-asyncmdnsresolver==0.1.1", "aiohttp-asyncmdnsresolver==0.1.1",
@ -111,7 +111,7 @@ dependencies = [
"SQLAlchemy==2.0.41", "SQLAlchemy==2.0.41",
"standard-aifc==3.13.0", "standard-aifc==3.13.0",
"standard-telnetlib==3.13.0", "standard-telnetlib==3.13.0",
"typing-extensions>=4.13.0,<5.0", "typing-extensions>=4.14.0,<5.0",
"ulid-transform==1.4.0", "ulid-transform==1.4.0",
# Constrain urllib3 to ensure we deal with CVE-2020-26137 and CVE-2021-33503 # Constrain urllib3 to ensure we deal with CVE-2020-26137 and CVE-2021-33503
# Temporary setting an upper bound, to prevent compat issues with urllib3>=2 # Temporary setting an upper bound, to prevent compat issues with urllib3>=2
@ -530,18 +530,20 @@ filterwarnings = [
# https://github.com/DataDog/datadogpy/pull/290 - >=0.23.0 # https://github.com/DataDog/datadogpy/pull/290 - >=0.23.0
"ignore:invalid escape sequence:SyntaxWarning:.*datadog.dogstatsd.base", "ignore:invalid escape sequence:SyntaxWarning:.*datadog.dogstatsd.base",
# https://github.com/DataDog/datadogpy/pull/566/files - >=0.37.0 # https://github.com/DataDog/datadogpy/pull/566/files - >=0.37.0
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:datadog.util.compat", "ignore:pkg_resources is deprecated as an API:UserWarning:datadog.util.compat",
# https://github.com/httplib2/httplib2/pull/226 - >=0.21.0 # https://github.com/httplib2/httplib2/pull/226 - >=0.21.0
"ignore:ssl.PROTOCOL_TLS is deprecated:DeprecationWarning:httplib2", "ignore:ssl.PROTOCOL_TLS is deprecated:DeprecationWarning:httplib2",
# https://github.com/influxdata/influxdb-client-python/issues/603 >=1.45.0
# https://github.com/influxdata/influxdb-client-python/pull/652
"ignore:datetime.*utcfromtimestamp\\(\\) is deprecated and scheduled for removal:DeprecationWarning:influxdb_client.client.write.point",
# https://github.com/majuss/lupupy/pull/15 - >0.3.2 # https://github.com/majuss/lupupy/pull/15 - >0.3.2
"ignore:\"is not\" with 'str' literal. Did you mean \"!=\"?:SyntaxWarning:.*lupupy.devices.alarm", "ignore:\"is not\" with 'str' literal. Did you mean \"!=\"?:SyntaxWarning:.*lupupy.devices.alarm",
# https://github.com/nextcord/nextcord/pull/1095 - >=3.0.0 # https://github.com/nextcord/nextcord/pull/1095 - >=3.0.0
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:nextcord.health_check", "ignore:pkg_resources is deprecated as an API:UserWarning:nextcord.health_check",
# https://github.com/vacanza/python-holidays/discussions/1800 - >1.0.0 # https://github.com/vacanza/python-holidays/discussions/1800 - >1.0.0
"ignore::DeprecationWarning:holidays", "ignore::DeprecationWarning:holidays",
# https://github.com/ReactiveX/RxPY/pull/716 - >4.0.4
"ignore:datetime.*utcfromtimestamp\\(\\) is deprecated and scheduled for removal:DeprecationWarning:reactivex.internal.constants",
# https://github.com/postlund/pyatv/issues/2645 - >0.16.0
# https://github.com/postlund/pyatv/pull/2664
"ignore:Protobuf gencode .* exactly one major version older than the runtime version 6.* at pyatv:UserWarning:google.protobuf.runtime_version",
# https://github.com/rytilahti/python-miio/pull/1809 - >=0.6.0.dev0 # https://github.com/rytilahti/python-miio/pull/1809 - >=0.6.0.dev0
"ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:miio.protocol", "ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:miio.protocol",
"ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:miio.miioprotocol", "ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:miio.miioprotocol",
@ -549,6 +551,8 @@ filterwarnings = [
"ignore:functools.partial will be a method descriptor in future Python versions; wrap it in enum.member\\(\\) if you want to preserve the old behavior:FutureWarning:miio.miot_device", "ignore:functools.partial will be a method descriptor in future Python versions; wrap it in enum.member\\(\\) if you want to preserve the old behavior:FutureWarning:miio.miot_device",
# https://github.com/okunishinishi/python-stringcase/commit/6a5c5bbd3fe5337862abc7fd0853a0f36e18b2e1 - >1.2.0 # https://github.com/okunishinishi/python-stringcase/commit/6a5c5bbd3fe5337862abc7fd0853a0f36e18b2e1 - >1.2.0
"ignore:invalid escape sequence:SyntaxWarning:.*stringcase", "ignore:invalid escape sequence:SyntaxWarning:.*stringcase",
# https://github.com/xchwarze/samsung-tv-ws-api/pull/151 - >2.7.2 - 2024-12-06 # wrong stacklevel in aiohttp
"ignore:verify_ssl is deprecated, use ssl=False instead:DeprecationWarning:aiohttp.client",
# -- other # -- other
# Locale changes might take some time to resolve upstream # Locale changes might take some time to resolve upstream
@ -580,6 +584,8 @@ filterwarnings = [
"ignore:getReadersFromUrls is deprecated. Please use get_readers_from_urls instead:DeprecationWarning:pysnmp.smi.compiler", "ignore:getReadersFromUrls is deprecated. Please use get_readers_from_urls instead:DeprecationWarning:pysnmp.smi.compiler",
# https://github.com/Python-roborock/python-roborock/issues/305 - 2.18.0 - 2025-04-06 # https://github.com/Python-roborock/python-roborock/issues/305 - 2.18.0 - 2025-04-06
"ignore:Callback API version 1 is deprecated, update to latest version:DeprecationWarning:roborock.cloud_api", "ignore:Callback API version 1 is deprecated, update to latest version:DeprecationWarning:roborock.cloud_api",
# https://github.com/Teslemetry/python-tesla-fleet-api - v1.1.1 - 2025-05-29
"ignore:Protobuf gencode .* exactly one major version older than the runtime version 6.* at (car_server|common|errors|keys|managed_charging|signatures|universal_message|vcsec|vehicle):UserWarning:google.protobuf.runtime_version",
# https://github.com/briis/pyweatherflowudp/blob/v1.4.5/pyweatherflowudp/const.py#L20 - v1.4.5 - 2023-10-10 # https://github.com/briis/pyweatherflowudp/blob/v1.4.5/pyweatherflowudp/const.py#L20 - v1.4.5 - 2023-10-10
"ignore:This function will be removed in future versions of pint:DeprecationWarning:pyweatherflowudp.const", "ignore:This function will be removed in future versions of pint:DeprecationWarning:pyweatherflowudp.const",
# New in aiohttp - v3.9.0 # New in aiohttp - v3.9.0
@ -602,14 +608,12 @@ filterwarnings = [
# https://pypi.org/project/sleekxmppfs/ - v1.4.1 - 2022-08-18 # https://pypi.org/project/sleekxmppfs/ - v1.4.1 - 2022-08-18
"ignore:invalid escape sequence:SyntaxWarning:.*sleekxmppfs.thirdparty.mini_dateutil", # codespell:ignore thirdparty "ignore:invalid escape sequence:SyntaxWarning:.*sleekxmppfs.thirdparty.mini_dateutil", # codespell:ignore thirdparty
# - pkg_resources # - pkg_resources
# https://pypi.org/project/aiomusiccast/ - v0.14.8 - 2023-03-20
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:aiomusiccast",
# https://github.com/eavanvalkenburg/pysiaalarm/blob/v3.1.1/src/pysiaalarm/data/data.py#L7 - v3.1.1 - 2023-04-17 # https://github.com/eavanvalkenburg/pysiaalarm/blob/v3.1.1/src/pysiaalarm/data/data.py#L7 - v3.1.1 - 2023-04-17
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:pysiaalarm.data.data", "ignore:pkg_resources is deprecated as an API:UserWarning:pysiaalarm.data.data",
# https://pypi.org/project/pybotvac/ - v0.0.26 - 2025-02-26 # https://pypi.org/project/pybotvac/ - v0.0.26 - 2025-02-26
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:pybotvac.version", "ignore:pkg_resources is deprecated as an API:UserWarning:pybotvac.version",
# https://github.com/home-assistant-ecosystem/python-mystrom/blob/2.2.0/pymystrom/__init__.py#L10 - v2.2.0 - 2023-05-21 # https://github.com/home-assistant-ecosystem/python-mystrom/blob/2.2.0/pymystrom/__init__.py#L10 - v2.2.0 - 2023-05-21
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:pymystrom", "ignore:pkg_resources is deprecated as an API:UserWarning:pymystrom",
# -- New in Python 3.13 # -- New in Python 3.13
# https://github.com/kurtmckee/feedparser/pull/389 - >6.0.11 # https://github.com/kurtmckee/feedparser/pull/389 - >6.0.11
@ -640,8 +644,6 @@ filterwarnings = [
"ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:directv.models", "ignore:datetime.*utcnow\\(\\) is deprecated and scheduled for removal:DeprecationWarning:directv.models",
# https://pypi.org/project/enocean/ - v0.50.1 (installed) -> v0.60.1 - 2021-06-18 # https://pypi.org/project/enocean/ - v0.50.1 (installed) -> v0.60.1 - 2021-06-18
"ignore:It looks like you're using an HTML parser to parse an XML document:UserWarning:enocean.protocol.eep", "ignore:It looks like you're using an HTML parser to parse an XML document:UserWarning:enocean.protocol.eep",
# https://pypi.org/project/httpsig/ - v1.3.0 - 2018-11-28
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:httpsig",
# https://pypi.org/project/influxdb/ - v5.3.2 - 2024-04-18 (archived) # https://pypi.org/project/influxdb/ - v5.3.2 - 2024-04-18 (archived)
"ignore:datetime.*utcfromtimestamp\\(\\) is deprecated and scheduled for removal:DeprecationWarning:influxdb.line_protocol", "ignore:datetime.*utcfromtimestamp\\(\\) is deprecated and scheduled for removal:DeprecationWarning:influxdb.line_protocol",
# https://pypi.org/project/lark-parser/ - v0.12.0 - 2021-08-30 -> moved to `lark` # https://pypi.org/project/lark-parser/ - v0.12.0 - 2021-08-30 -> moved to `lark`
@ -657,7 +659,7 @@ filterwarnings = [
# https://pypi.org/project/opuslib/ - v3.0.1 - 2018-01-16 # https://pypi.org/project/opuslib/ - v3.0.1 - 2018-01-16
"ignore:\"is not\" with 'int' literal. Did you mean \"!=\"?:SyntaxWarning:.*opuslib.api.decoder", "ignore:\"is not\" with 'int' literal. Did you mean \"!=\"?:SyntaxWarning:.*opuslib.api.decoder",
# https://pypi.org/project/pilight/ - v0.1.1 - 2016-10-19 # https://pypi.org/project/pilight/ - v0.1.1 - 2016-10-19
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:pilight", "ignore:pkg_resources is deprecated as an API:UserWarning:pilight",
# https://pypi.org/project/plumlightpad/ - v0.0.11 - 2018-10-16 # https://pypi.org/project/plumlightpad/ - v0.0.11 - 2018-10-16
"ignore:invalid escape sequence:SyntaxWarning:.*plumlightpad.plumdiscovery", "ignore:invalid escape sequence:SyntaxWarning:.*plumlightpad.plumdiscovery",
"ignore:\"is\" with 'int' literal. Did you mean \"==\"?:SyntaxWarning:.*plumlightpad.(lightpad|logicalload)", "ignore:\"is\" with 'int' literal. Did you mean \"==\"?:SyntaxWarning:.*plumlightpad.(lightpad|logicalload)",
@ -672,8 +674,6 @@ filterwarnings = [
# https://pypi.org/project/pyqwikswitch/ - v0.94 - 2019-08-19 # https://pypi.org/project/pyqwikswitch/ - v0.94 - 2019-08-19
"ignore:client.loop property is deprecated:DeprecationWarning:pyqwikswitch.async_", "ignore:client.loop property is deprecated:DeprecationWarning:pyqwikswitch.async_",
"ignore:with timeout\\(\\) is deprecated:DeprecationWarning:pyqwikswitch.async_", "ignore:with timeout\\(\\) is deprecated:DeprecationWarning:pyqwikswitch.async_",
# https://pypi.org/project/Rx/ - v3.2.0 - 2021-04-25
"ignore:datetime.*utcfromtimestamp\\(\\) is deprecated and scheduled for removal:DeprecationWarning:rx.internal.constants",
# https://pypi.org/project/rxv/ - v0.7.0 - 2021-10-10 # https://pypi.org/project/rxv/ - v0.7.0 - 2021-10-10
"ignore:defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead:DeprecationWarning:rxv.ssdp", "ignore:defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead:DeprecationWarning:rxv.ssdp",
] ]

4
requirements.txt generated
View File

@ -5,7 +5,7 @@
# Home Assistant Core # Home Assistant Core
aiodns==3.4.0 aiodns==3.4.0
aiohasupervisor==0.3.1 aiohasupervisor==0.3.1
aiohttp==3.12.6 aiohttp==3.12.7
aiohttp_cors==0.8.1 aiohttp_cors==0.8.1
aiohttp-fast-zlib==0.2.3 aiohttp-fast-zlib==0.2.3
aiohttp-asyncmdnsresolver==0.1.1 aiohttp-asyncmdnsresolver==0.1.1
@ -51,7 +51,7 @@ securetar==2025.2.1
SQLAlchemy==2.0.41 SQLAlchemy==2.0.41
standard-aifc==3.13.0 standard-aifc==3.13.0
standard-telnetlib==3.13.0 standard-telnetlib==3.13.0
typing-extensions>=4.13.0,<5.0 typing-extensions>=4.14.0,<5.0
ulid-transform==1.4.0 ulid-transform==1.4.0
urllib3>=1.26.5,<2 urllib3>=1.26.5,<2
uv==0.7.1 uv==0.7.1

16
requirements_all.txt generated
View File

@ -182,7 +182,7 @@ aioairzone-cloud==0.6.12
aioairzone==1.0.0 aioairzone==1.0.0
# homeassistant.components.amazon_devices # homeassistant.components.amazon_devices
aioamazondevices==3.0.4 aioamazondevices==3.0.5
# homeassistant.components.ambient_network # homeassistant.components.ambient_network
# homeassistant.components.ambient_station # homeassistant.components.ambient_station
@ -268,7 +268,7 @@ aiohasupervisor==0.3.1
aiohomeconnect==0.17.1 aiohomeconnect==0.17.1
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==3.2.14 aiohomekit==3.2.15
# homeassistant.components.mcp_server # homeassistant.components.mcp_server
aiohttp_sse==2.2.0 aiohttp_sse==2.2.0
@ -289,7 +289,7 @@ aiokafka==0.10.0
aiokef==0.2.16 aiokef==0.2.16
# homeassistant.components.rehlko # homeassistant.components.rehlko
aiokem==0.5.12 aiokem==1.0.1
# homeassistant.components.lifx # homeassistant.components.lifx
aiolifx-effects==0.3.2 aiolifx-effects==0.3.2
@ -1026,7 +1026,7 @@ gitterpy==0.1.7
glances-api==0.8.0 glances-api==0.8.0
# homeassistant.components.go2rtc # homeassistant.components.go2rtc
go2rtc-client==0.1.3b0 go2rtc-client==0.2.1
# homeassistant.components.goalzero # homeassistant.components.goalzero
goalzero==0.2.2 goalzero==0.2.2
@ -1572,7 +1572,7 @@ oemthermostat==1.1.1
ohme==1.5.1 ohme==1.5.1
# homeassistant.components.ollama # homeassistant.components.ollama
ollama==0.4.7 ollama==0.5.1
# homeassistant.components.omnilogic # homeassistant.components.omnilogic
omnilogic==0.4.5 omnilogic==0.4.5
@ -1841,7 +1841,7 @@ pyasuswrt==0.1.21
pyatag==0.3.5.3 pyatag==0.3.5.3
# homeassistant.components.netatmo # homeassistant.components.netatmo
pyatmo==9.2.0 pyatmo==9.2.1
# homeassistant.components.apple_tv # homeassistant.components.apple_tv
pyatv==0.16.0 pyatv==0.16.0
@ -2221,7 +2221,7 @@ pyotgw==2.2.2
pyotp==2.8.0 pyotp==2.8.0
# homeassistant.components.overkiz # homeassistant.components.overkiz
pyoverkiz==1.17.1 pyoverkiz==1.17.2
# homeassistant.components.onewire # homeassistant.components.onewire
pyownet==0.10.0.post1 pyownet==0.10.0.post1
@ -2434,7 +2434,7 @@ python-google-drive-api==0.1.0
python-homeassistant-analytics==0.9.0 python-homeassistant-analytics==0.9.0
# homeassistant.components.homewizard # homeassistant.components.homewizard
python-homewizard-energy==v8.3.2 python-homewizard-energy==8.3.3
# homeassistant.components.hp_ilo # homeassistant.components.hp_ilo
python-hpilo==4.4.3 python-hpilo==4.4.3

View File

@ -10,7 +10,7 @@
astroid==3.3.10 astroid==3.3.10
coverage==7.8.2 coverage==7.8.2
freezegun==1.5.2 freezegun==1.5.2
go2rtc-client==0.1.3b0 go2rtc-client==0.2.1
license-expression==30.4.1 license-expression==30.4.1
mock-open==1.4.0 mock-open==1.4.0
mypy-dev==1.17.0a2 mypy-dev==1.17.0a2

View File

@ -170,7 +170,7 @@ aioairzone-cloud==0.6.12
aioairzone==1.0.0 aioairzone==1.0.0
# homeassistant.components.amazon_devices # homeassistant.components.amazon_devices
aioamazondevices==3.0.4 aioamazondevices==3.0.5
# homeassistant.components.ambient_network # homeassistant.components.ambient_network
# homeassistant.components.ambient_station # homeassistant.components.ambient_station
@ -253,7 +253,7 @@ aiohasupervisor==0.3.1
aiohomeconnect==0.17.1 aiohomeconnect==0.17.1
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==3.2.14 aiohomekit==3.2.15
# homeassistant.components.mcp_server # homeassistant.components.mcp_server
aiohttp_sse==2.2.0 aiohttp_sse==2.2.0
@ -271,7 +271,7 @@ aioimmich==0.8.0
aiokafka==0.10.0 aiokafka==0.10.0
# homeassistant.components.rehlko # homeassistant.components.rehlko
aiokem==0.5.12 aiokem==1.0.1
# homeassistant.components.lifx # homeassistant.components.lifx
aiolifx-effects==0.3.2 aiolifx-effects==0.3.2
@ -887,7 +887,7 @@ gios==6.0.0
glances-api==0.8.0 glances-api==0.8.0
# homeassistant.components.go2rtc # homeassistant.components.go2rtc
go2rtc-client==0.1.3b0 go2rtc-client==0.2.1
# homeassistant.components.goalzero # homeassistant.components.goalzero
goalzero==0.2.2 goalzero==0.2.2
@ -1334,7 +1334,7 @@ odp-amsterdam==6.1.1
ohme==1.5.1 ohme==1.5.1
# homeassistant.components.ollama # homeassistant.components.ollama
ollama==0.4.7 ollama==0.5.1
# homeassistant.components.omnilogic # homeassistant.components.omnilogic
omnilogic==0.4.5 omnilogic==0.4.5
@ -1540,7 +1540,7 @@ pyasuswrt==0.1.21
pyatag==0.3.5.3 pyatag==0.3.5.3
# homeassistant.components.netatmo # homeassistant.components.netatmo
pyatmo==9.2.0 pyatmo==9.2.1
# homeassistant.components.apple_tv # homeassistant.components.apple_tv
pyatv==0.16.0 pyatv==0.16.0
@ -1842,7 +1842,7 @@ pyotgw==2.2.2
pyotp==2.8.0 pyotp==2.8.0
# homeassistant.components.overkiz # homeassistant.components.overkiz
pyoverkiz==1.17.1 pyoverkiz==1.17.2
# homeassistant.components.onewire # homeassistant.components.onewire
pyownet==0.10.0.post1 pyownet==0.10.0.post1
@ -2007,7 +2007,7 @@ python-google-drive-api==0.1.0
python-homeassistant-analytics==0.9.0 python-homeassistant-analytics==0.9.0
# homeassistant.components.homewizard # homeassistant.components.homewizard
python-homewizard-energy==v8.3.2 python-homewizard-energy==8.3.3
# homeassistant.components.izone # homeassistant.components.izone
python-izone==1.2.9 python-izone==1.2.9

View File

@ -1,5 +1,5 @@
# Automatically generated from .pre-commit-config.yaml by gen_requirements_all.py, do not edit # Automatically generated from .pre-commit-config.yaml by gen_requirements_all.py, do not edit
codespell==2.4.1 codespell==2.4.1
ruff==0.11.0 ruff==0.11.12
yamllint==1.35.1 yamllint==1.37.1

View File

@ -249,6 +249,10 @@ GENERATED_MESSAGE = (
f"# Automatically generated by {Path(__file__).name}, do not edit\n\n" f"# Automatically generated by {Path(__file__).name}, do not edit\n\n"
) )
MAP_HOOK_ID_TO_PACKAGE = {
"ruff-check": "ruff",
}
IGNORE_PRE_COMMIT_HOOK_ID = ( IGNORE_PRE_COMMIT_HOOK_ID = (
"check-executables-have-shebangs", "check-executables-have-shebangs",
"check-json", "check-json",
@ -523,7 +527,8 @@ def requirements_pre_commit_output() -> str:
rev: str = repo["rev"] rev: str = repo["rev"]
for hook in repo["hooks"]: for hook in repo["hooks"]:
if hook["id"] not in IGNORE_PRE_COMMIT_HOOK_ID: if hook["id"] not in IGNORE_PRE_COMMIT_HOOK_ID:
reqs.append(f"{hook['id']}=={rev.lstrip('v')}") pkg = MAP_HOOK_ID_TO_PACKAGE.get(hook["id"]) or hook["id"]
reqs.append(f"{pkg}=={rev.lstrip('v')}")
reqs.extend(x for x in hook.get("additional_dependencies", ())) reqs.extend(x for x in hook.get("additional_dependencies", ()))
output = [ output = [
f"# Automatically generated " f"# Automatically generated "

View File

@ -24,8 +24,8 @@ RUN --mount=from=ghcr.io/astral-sh/uv:0.7.1,source=/uv,target=/bin/uv \
--no-cache \ --no-cache \
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \ -c /usr/src/homeassistant/homeassistant/package_constraints.txt \
-r /usr/src/homeassistant/requirements.txt \ -r /usr/src/homeassistant/requirements.txt \
stdlib-list==0.10.0 pipdeptree==2.26.1 tqdm==4.67.1 ruff==0.11.0 \ stdlib-list==0.10.0 pipdeptree==2.26.1 tqdm==4.67.1 ruff==0.11.12 \
PyTurboJPEG==1.8.0 go2rtc-client==0.1.3b0 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.5.28 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2 PyTurboJPEG==1.8.0 go2rtc-client==0.2.1 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.5.28 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2
LABEL "name"="hassfest" LABEL "name"="hassfest"
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>" LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"

View File

@ -26,6 +26,7 @@ from .model import Config, Integration
PACKAGE_CHECK_VERSION_RANGE = { PACKAGE_CHECK_VERSION_RANGE = {
"aiohttp": "SemVer", "aiohttp": "SemVer",
"attrs": "CalVer", "attrs": "CalVer",
"awesomeversion": "CalVer",
"grpcio": "SemVer", "grpcio": "SemVer",
"httpx": "SemVer", "httpx": "SemVer",
"mashumaro": "SemVer", "mashumaro": "SemVer",
@ -40,13 +41,9 @@ PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
# - domain is the integration domain # - domain is the integration domain
# - package is the package (can be transitive) referencing the dependency # - package is the package (can be transitive) referencing the dependency
# - dependencyX should be the name of the referenced dependency # - dependencyX should be the name of the referenced dependency
"ollama": { "mealie": {
# https://github.com/ollama/ollama-python/pull/445 (not yet released) # https://github.com/joostlek/python-mealie/pull/490
"ollama": {"httpx"} "aiomealie": {"awesomeversion"}
},
"overkiz": {
# https://github.com/iMicknl/python-overkiz-api/issues/1644 (not yet released)
"pyoverkiz": {"attrs"},
}, },
} }
@ -333,14 +330,6 @@ PYTHON_VERSION_CHECK_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
# https://github.com/EuleMitKeule/eq3btsmart/releases/tag/2.0.0 # https://github.com/EuleMitKeule/eq3btsmart/releases/tag/2.0.0
"homeassistant": {"eq3btsmart"} "homeassistant": {"eq3btsmart"}
}, },
"homekit_controller": {
# https://github.com/Jc2k/aiohomekit/issues/456
"homeassistant": {"aiohomekit"}
},
"netatmo": {
# https://github.com/jabesq-org/pyatmo/pull/533 (not yet released)
"homeassistant": {"pyatmo"}
},
"python_script": { "python_script": {
# Security audits are needed for each Python version # Security audits are needed for each Python version
"homeassistant": {"restrictedpython"} "homeassistant": {"restrictedpython"}

View File

@ -196,6 +196,7 @@ EXCEPTIONS = {
"maxcube-api", # https://github.com/uebelack/python-maxcube-api/pull/48 "maxcube-api", # https://github.com/uebelack/python-maxcube-api/pull/48
"neurio", # https://github.com/jordanh/neurio-python/pull/13 "neurio", # https://github.com/jordanh/neurio-python/pull/13
"nsw-fuel-api-client", # https://github.com/nickw444/nsw-fuel-api-client/pull/14 "nsw-fuel-api-client", # https://github.com/nickw444/nsw-fuel-api-client/pull/14
"ollama", # https://github.com/ollama/ollama-python/pull/526
"pigpio", # https://github.com/joan2937/pigpio/pull/608 "pigpio", # https://github.com/joan2937/pigpio/pull/608
"pymitv", # MIT "pymitv", # MIT
"pybbox", # https://github.com/HydrelioxGitHub/pybbox/pull/5 "pybbox", # https://github.com/HydrelioxGitHub/pybbox/pull/5

View File

@ -15,7 +15,7 @@ printf "%s\n" $files
echo "==============" echo "=============="
echo "LINT with ruff" echo "LINT with ruff"
echo "==============" echo "=============="
pre-commit run ruff --files $files pre-commit run ruff-check --files $files
echo "================" echo "================"
echo "LINT with pylint" echo "LINT with pylint"
echo "================" echo "================"