Compare commits

...

56 Commits

Author SHA1 Message Date
Ingo Fischer
707c9dd0c6 Update requirements files 2026-02-21 12:56:44 +01:00
Ingo Fischer
629045ef74 Replace Matter python client
Move to matter-python-client library
2026-02-21 11:50:36 +01:00
Josef Zweck
452b0775ee Revert "Replace "add-on" with "app" in zwave_me" (#163701) 2026-02-21 11:13:23 +01:00
Norbert Rittel
dc5caf307b Replace "add-on" with "app" in zwave_me (#163698) 2026-02-21 11:04:45 +01:00
Norbert Rittel
686bcb3199 Replace "add-on" with "app" in homeassistant_hardware (#163696) 2026-02-21 11:04:17 +01:00
hanwg
047d5735d8 Cleanup error handling for Telegram bot (#163689) 2026-02-21 09:19:06 +01:00
Nathan Spencer
c3b0f7ba55 Bump pylitterbot to 2025.1.0 (#163691) 2026-02-21 09:17:59 +01:00
Manu
11f0cd690e Bump aiontfy to 0.8.0 (#163693) 2026-02-21 09:16:14 +01:00
Norbert Rittel
048fbba36d Replace "add-on" with "app" in matter (#163695) 2026-02-21 08:54:49 +01:00
epenet
a791797a6f Mark entity icon type hints as mandatory (#163617) 2026-02-20 22:48:56 +01:00
Erwin Douna
aa2bb44f0e Bump pyportainer 1.0.27 (#163613)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-02-20 21:33:13 +01:00
Marc Mueller
6ecbaa979a Fix hassfest requirements check (#163681) 2026-02-20 20:36:07 +01:00
epenet
6115a4c1fb Use shorthand attributes in swiss_hydrological_data (#163607) 2026-02-20 19:56:39 +01:00
Joost Lekkerkerker
f6459453ed Add integration_type hub to surepetcare (#163646) 2026-02-20 19:51:02 +01:00
epenet
eeb7ce3725 Improve type hints in homematic hub (#163614) 2026-02-20 19:49:23 +01:00
Joost Lekkerkerker
f020948e2d Add integration_type hub to tradfri (#163673)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-20 19:48:55 +01:00
Joost Lekkerkerker
0711176f9c Add integration_type device to tilt_ble (#163666) 2026-02-20 19:48:35 +01:00
Joost Lekkerkerker
cd26901386 Add integration_type service to todoist (#163668) 2026-02-20 19:46:51 +01:00
Joost Lekkerkerker
3c1b7ada9a Add integration_type device to tolo (#163670) 2026-02-20 19:46:36 +01:00
Joost Lekkerkerker
debf07e3fc Add integration_type device to toon (#163671) 2026-02-20 19:46:07 +01:00
Joost Lekkerkerker
541cc808b0 Add integration_type hub to totalconnect (#163672) 2026-02-20 19:45:21 +01:00
Joost Lekkerkerker
46b0eaecf6 Add integration_type service to trafikverket_camera (#163674) 2026-02-20 19:43:48 +01:00
Joost Lekkerkerker
35e770b998 Add integration_type service to trafikverket_ferry (#163675) 2026-02-20 19:43:14 +01:00
Joost Lekkerkerker
ed9ad950d9 Add integration_type service to trafikverket_train (#163676) 2026-02-20 19:42:55 +01:00
Joost Lekkerkerker
02058afb10 Add integration_type service to trafikverket_weatherstation (#163677) 2026-02-20 19:42:39 +01:00
Joost Lekkerkerker
3f6bfa96fc Add integration_type hub to tellduslive (#163661) 2026-02-20 19:41:34 +01:00
Joost Lekkerkerker
430f064243 Add integration_type device to tesla_wall_connector (#163662) 2026-02-20 19:40:20 +01:00
Joost Lekkerkerker
08adb88c6b Add integration_type device to thermobeacon (#163663) 2026-02-20 19:39:43 +01:00
Joost Lekkerkerker
14b6269dbf Add integration_type device to thermopro (#163664) 2026-02-20 19:39:05 +01:00
Joost Lekkerkerker
19b1fc6561 Add integration_type hub to tibber (#163665) 2026-02-20 19:37:34 +01:00
Joost Lekkerkerker
b6e83d22e3 Add integration_type device to syncthru (#163658) 2026-02-20 19:36:19 +01:00
Joost Lekkerkerker
7cd48ef079 Add integration_type device to tami4 (#163659) 2026-02-20 19:35:29 +01:00
Joost Lekkerkerker
2a03d95bcd Add integration_type service to telegram_bot (#163660) 2026-02-20 19:34:39 +01:00
Joost Lekkerkerker
e7e8c7a53a Add integration_type device to togrill (#163669) 2026-02-20 19:11:57 +01:00
Joost Lekkerkerker
6ce28987ab Add integration_type service to syncthing (#163651) 2026-02-20 16:27:23 +01:00
Joost Lekkerkerker
da537ddb8b Add integration_type device to steamist (#163640) 2026-02-20 16:26:59 +01:00
Joost Lekkerkerker
03f81e4a09 Add integration_type hub to starline (#163638) 2026-02-20 16:25:58 +01:00
Joost Lekkerkerker
88bc6165b5 Add integration_type device to starlink (#163639) 2026-02-20 16:25:33 +01:00
Joost Lekkerkerker
a1f35ed3c4 Add integration_type hub to switcher_kis (#163650) 2026-02-20 17:23:57 +02:00
Joost Lekkerkerker
c15a804ab4 Add integration_type service to srp_energy (#163636) 2026-02-20 16:23:39 +01:00
Joost Lekkerkerker
34f1c4cbe0 Add integration_type device to soundtouch (#163634) 2026-02-20 16:23:00 +01:00
Joost Lekkerkerker
bf950e4916 Add integration_type service to splunk (#163635) 2026-02-20 16:22:33 +01:00
Joost Lekkerkerker
47eba50b4a Add integration_type service to sonarr (#163632) 2026-02-20 16:22:07 +01:00
Joost Lekkerkerker
8ff06f3c72 Add integration_type hub to soma (#163630) 2026-02-20 16:21:35 +01:00
Joost Lekkerkerker
d2918586f9 Add integration_type device to solax (#163629) 2026-02-20 16:21:09 +01:00
Joost Lekkerkerker
8c3e72b53d Add integration_type device to snooz (#163627) 2026-02-20 16:20:31 +01:00
Joost Lekkerkerker
3143d9c4fd Add integration_type hub to snoo (#163626) 2026-02-20 16:20:01 +01:00
Joost Lekkerkerker
04621a2e58 Add integration_type hub to switchbee (#163648) 2026-02-20 16:19:28 +01:00
Joost Lekkerkerker
9b6e6a688d Add integration_type service to swiss_public_transport (#163647) 2026-02-20 16:18:57 +01:00
Joost Lekkerkerker
2bf5f67ecd Add integration_type service to suez_water (#163644) 2026-02-20 16:18:20 +01:00
Joost Lekkerkerker
522f63cdab Add integration_type hub to sunricher_dali (#163645) 2026-02-20 16:18:03 +01:00
Joost Lekkerkerker
03f5e6d6a3 Add integration_type device to songpal (#163633) 2026-02-20 16:17:47 +01:00
Joost Lekkerkerker
c2ba5d87d5 Add integration_type hub to subaru (#163643) 2026-02-20 16:17:03 +01:00
Joost Lekkerkerker
6a9fd67e05 Add integration_type hub to somfy_mylink (#163631) 2026-02-20 16:16:35 +01:00
Joost Lekkerkerker
69db5787ec Add integration_type device to stiebel_eltron (#163641) 2026-02-20 16:15:39 +01:00
Joost Lekkerkerker
8a38bace90 Add integration_type service to streamlabswater (#163642) 2026-02-20 16:15:05 +01:00
60 changed files with 192 additions and 208 deletions

View File

@@ -37,7 +37,7 @@ on:
type: boolean
env:
CACHE_VERSION: 2
CACHE_VERSION: 3
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.3"

View File

@@ -4,16 +4,16 @@
"abort": {
"fw_download_failed": "{firmware_name} firmware for your {model} failed to download. Make sure Home Assistant has internet access and try again.",
"fw_install_failed": "{firmware_name} firmware failed to install, check Home Assistant logs for more information.",
"not_hassio_thread": "The OpenThread Border Router add-on can only be installed with Home Assistant OS. If you would like to use the {model} as a Thread border router, please manually set up OpenThread Border Router to communicate with it.",
"otbr_addon_already_running": "The OpenThread Border Router add-on is already running, it cannot be installed again.",
"otbr_still_using_stick": "This {model} is in use by the OpenThread Border Router add-on. If you use the Thread network, make sure you have alternative border routers. Uninstall the add-on and try again.",
"unsupported_firmware": "The radio firmware on your {model} could not be determined. Make sure that no other integration or add-on is currently trying to communicate with the device. If you are running Home Assistant OS in a virtual machine or in Docker, please make sure that permissions are set correctly for the device.",
"not_hassio_thread": "The OpenThread Border Router app can only be installed with Home Assistant OS. If you would like to use the {model} as a Thread border router, please manually set up OpenThread Border Router to communicate with it.",
"otbr_addon_already_running": "The OpenThread Border Router app is already running, it cannot be installed again.",
"otbr_still_using_stick": "This {model} is in use by the OpenThread Border Router app. If you use the Thread network, make sure you have alternative border routers. Uninstall the app and try again.",
"unsupported_firmware": "The radio firmware on your {model} could not be determined. Make sure that no other integration or app is currently trying to communicate with the device. If you are running Home Assistant OS in a virtual machine or in Docker, please make sure that permissions are set correctly for the device.",
"zha_still_using_stick": "This {model} is in use by the Zigbee Home Automation integration. Please migrate your Zigbee network to another adapter or delete the integration and try again."
},
"progress": {
"install_firmware": "Installing {firmware_name} firmware.\n\nDo not make any changes to your hardware or software until this finishes.",
"install_otbr_addon": "Installing add-on",
"start_otbr_addon": "Starting add-on"
"install_otbr_addon": "Installing app",
"start_otbr_addon": "Starting app"
},
"step": {
"confirm_otbr": {
@@ -34,7 +34,7 @@
"title": "Updating adapter"
},
"otbr_failed": {
"description": "The OpenThread Border Router add-on installation was unsuccessful. Ensure no other software is trying to communicate with the {model}, you have access to the Internet and can install other add-ons, and try again. Check the Supervisor logs if the problem persists.",
"description": "The OpenThread Border Router app installation was unsuccessful. Ensure no other software is trying to communicate with the {model}, you have access to the Internet and can install other apps, and try again. Check the Supervisor logs if the problem persists.",
"title": "Failed to set up OpenThread Border Router"
},
"pick_firmware": {
@@ -89,11 +89,11 @@
"silabs_multiprotocol_hardware": {
"options": {
"abort": {
"addon_already_running": "Failed to start the {addon_name} add-on because it is already running.",
"addon_info_failed": "Failed to get {addon_name} add-on info.",
"addon_install_failed": "Failed to install the {addon_name} add-on.",
"addon_already_running": "Failed to start the {addon_name} app because it is already running.",
"addon_info_failed": "Failed to get {addon_name} app info.",
"addon_install_failed": "Failed to install the {addon_name} app.",
"addon_set_config_failed": "Failed to set {addon_name} configuration.",
"addon_start_failed": "Failed to start the {addon_name} add-on.",
"addon_start_failed": "Failed to start the {addon_name} app.",
"not_hassio": "The hardware options can only be configured on Home Assistant OS installations.",
"zha_migration_failed": "The ZHA migration did not succeed."
},
@@ -101,8 +101,8 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"progress": {
"install_addon": "Please wait while the {addon_name} add-on installation finishes. This can take several minutes.",
"start_addon": "Please wait while the {addon_name} add-on start completes. This may take some seconds."
"install_addon": "Please wait while the {addon_name} app installation finishes. This can take several minutes.",
"start_addon": "Please wait while the {addon_name} app start completes. This may take some seconds."
},
"step": {
"addon_installed_other_device": {
@@ -129,7 +129,7 @@
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]"
},
"install_addon": {
"title": "The Silicon Labs Multiprotocol add-on installation has started"
"title": "The Silicon Labs Multiprotocol app installation has started"
},
"notify_channel_change": {
"description": "A Zigbee and Thread channel change has been initiated and will finish in {delay_minutes} minutes.",
@@ -143,7 +143,7 @@
"title": "Reconfigure IEEE 802.15.4 radio multiprotocol support"
},
"start_addon": {
"title": "The Silicon Labs Multiprotocol add-on is starting."
"title": "The Silicon Labs Multiprotocol app is starting."
},
"uninstall_addon": {
"data": {

View File

@@ -3,6 +3,7 @@
from datetime import datetime
from functools import partial
import logging
from typing import Any
from pyhomematic import HMConnection
import voluptuous as vol
@@ -215,8 +216,11 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DATA_CONF] = remotes = {}
hass.data[DATA_STORE] = set()
interfaces: dict[str, dict[str, Any]] = conf[CONF_INTERFACES]
hosts: dict[str, dict[str, Any]] = conf[CONF_HOSTS]
# Create hosts-dictionary for pyhomematic
for rname, rconfig in conf[CONF_INTERFACES].items():
for rname, rconfig in interfaces.items():
remotes[rname] = {
"ip": rconfig.get(CONF_HOST),
"port": rconfig.get(CONF_PORT),
@@ -232,7 +236,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"connect": True,
}
for sname, sconfig in conf[CONF_HOSTS].items():
for sname, sconfig in hosts.items():
remotes[sname] = {
"ip": sconfig.get(CONF_HOST),
"port": sconfig[CONF_PORT],
@@ -258,7 +262,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, hass.data[DATA_HOMEMATIC].stop)
# Init homematic hubs
entity_hubs = [HMHub(hass, homematic, hub_name) for hub_name in conf[CONF_HOSTS]]
entity_hubs = [HMHub(hass, homematic, hub_name) for hub_name in hosts]
def _hm_service_virtualkey(service: ServiceCall) -> None:
"""Service to handle virtualkey servicecalls."""
@@ -294,7 +298,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
def _service_handle_value(service: ServiceCall) -> None:
"""Service to call setValue method for HomeMatic system variable."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
entity_ids: list[str] | None = service.data.get(ATTR_ENTITY_ID)
name = service.data[ATTR_NAME]
value = service.data[ATTR_VALUE]

View File

@@ -11,6 +11,7 @@ from pyhomematic import HMConnection
from pyhomematic.devicetypes.generic import HMGeneric
from homeassistant.const import ATTR_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.event import track_time_interval
@@ -199,14 +200,14 @@ class HMHub(Entity):
_attr_should_poll = False
def __init__(self, hass, homematic, name):
def __init__(self, hass: HomeAssistant, homematic: HMConnection, name: str) -> None:
"""Initialize HomeMatic hub."""
self.hass = hass
self.entity_id = f"{DOMAIN}.{name.lower()}"
self._homematic = homematic
self._variables = {}
self._variables: dict[str, Any] = {}
self._name = name
self._state = None
self._state: int | None = None
# Load data
track_time_interval(self.hass, self._update_hub, SCAN_INTERVAL_HUB)
@@ -216,12 +217,12 @@ class HMHub(Entity):
self.hass.add_job(self._update_variables, None)
@property
def name(self):
def name(self) -> str:
"""Return the name of the device."""
return self._name
@property
def state(self):
def state(self) -> int | None:
"""Return the state of the entity."""
return self._state
@@ -231,7 +232,7 @@ class HMHub(Entity):
return self._variables.copy()
@property
def icon(self):
def icon(self) -> str:
"""Return the icon to use in the frontend, if any."""
return "mdi:gradient-vertical"

View File

@@ -13,5 +13,5 @@
"iot_class": "cloud_push",
"loggers": ["pylitterbot"],
"quality_scale": "bronze",
"requirements": ["pylitterbot==2025.0.0"]
"requirements": ["pylitterbot==2025.1.0"]
}

View File

@@ -8,6 +8,6 @@
"documentation": "https://www.home-assistant.io/integrations/matter",
"integration_type": "hub",
"iot_class": "local_push",
"requirements": ["python-matter-server==8.1.2"],
"requirements": ["matter-python-client==0.4.1"],
"zeroconf": ["_matter._tcp.local.", "_matterc._udp.local."]
}

View File

@@ -1,14 +1,14 @@
{
"config": {
"abort": {
"addon_get_discovery_info_failed": "Failed to get Matter Server add-on discovery info.",
"addon_info_failed": "Failed to get Matter Server add-on info.",
"addon_install_failed": "Failed to install the Matter Server add-on.",
"addon_start_failed": "Failed to start the Matter Server add-on.",
"addon_get_discovery_info_failed": "Failed to get Matter Server app discovery info.",
"addon_info_failed": "Failed to get Matter Server app info.",
"addon_install_failed": "Failed to install the Matter Server app.",
"addon_start_failed": "Failed to start the Matter Server app.",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"not_matter_addon": "Discovered add-on is not the official Matter Server add-on.",
"not_matter_addon": "Discovered app is not the official Matter Server app.",
"reconfiguration_successful": "Successfully reconfigured the Matter integration."
},
"error": {
@@ -18,15 +18,15 @@
},
"flow_title": "{name}",
"progress": {
"install_addon": "Please wait while the Matter Server add-on installation finishes. This can take several minutes.",
"start_addon": "Please wait while the Matter Server add-on starts. This add-on is what powers Matter in Home Assistant. This may take some seconds."
"install_addon": "Please wait while the Matter Server app installation finishes. This can take several minutes.",
"start_addon": "Please wait while the Matter Server app starts. This app is what powers Matter in Home Assistant. This may take some seconds."
},
"step": {
"hassio_confirm": {
"title": "Set up the Matter integration with the Matter Server add-on"
"title": "Set up the Matter integration with the Matter Server app"
},
"install_addon": {
"title": "The add-on installation has started"
"title": "The app installation has started"
},
"manual": {
"data": {
@@ -35,13 +35,13 @@
},
"on_supervisor": {
"data": {
"use_addon": "Use the official Matter Server Supervisor add-on"
"use_addon": "Use the official Matter Server Supervisor app"
},
"description": "Do you want to use the official Matter Server Supervisor add-on?\n\nIf you are already running the Matter Server in another add-on, in a custom container, natively etc., then do not select this option.",
"description": "Do you want to use the official Matter Server Supervisor app?\n\nIf you are already running the Matter Server in another app, in a custom container, natively etc., then do not select this option.",
"title": "Select connection method"
},
"start_addon": {
"title": "Starting add-on."
"title": "Starting app."
}
}
},

View File

@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/ntfy",
"integration_type": "service",
"iot_class": "cloud_push",
"loggers": ["aionfty"],
"loggers": ["aiontfy"],
"quality_scale": "platinum",
"requirements": ["aiontfy==0.7.0"]
"requirements": ["aiontfy==0.8.0"]
}

View File

@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["pyportainer==1.0.23"]
"requirements": ["pyportainer==1.0.27"]
}

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Lash-L"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/snoo",
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["snoo"],
"quality_scale": "bronze",

View File

@@ -13,6 +13,7 @@
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/snooz",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["pysnooz==0.8.6"]
}

View File

@@ -4,6 +4,7 @@
"codeowners": ["@squishykid", "@Darsstar"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/solax",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["solax"],
"requirements": ["solax==3.2.3"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ratsept"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/soma",
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["api"],
"requirements": ["pysoma==0.0.12"]

View File

@@ -10,6 +10,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/somfy_mylink",
"integration_type": "hub",
"iot_class": "assumed_state",
"loggers": ["somfy_mylink_synergy"],
"requirements": ["somfy-mylink-synergy==1.0.6"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ctalkington"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sonarr",
"integration_type": "service",
"iot_class": "local_polling",
"loggers": ["aiopyarr"],
"requirements": ["aiopyarr==23.4.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@rytilahti", "@shenxn"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/songpal",
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["songpal"],
"requirements": ["python-songpal==0.16.2"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@kroimon"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/soundtouch",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["libsoundtouch"],
"requirements": ["libsoundtouch==0.8"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Bre77"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/splunk",
"integration_type": "service",
"iot_class": "local_push",
"loggers": ["hass_splunk"],
"quality_scale": "legacy",

View File

@@ -4,6 +4,7 @@
"codeowners": ["@briglx"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/srp_energy",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["srpenergy"],
"requirements": ["srpenergy==1.3.6"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@anonym-tsk"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/starline",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["starline"],
"requirements": ["starline==0.1.5"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@boswelja"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/starlink",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["starlink-grpc-core==1.2.3"]
}

View File

@@ -14,6 +14,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/steamist",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["aiosteamist", "discovery30303"],
"requirements": ["aiosteamist==1.0.1", "discovery30303==0.3.3"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@fucm", "@ThyMYthOS"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/stiebel_eltron",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["pymodbus", "pystiebeleltron"],
"requirements": ["pystiebeleltron==0.2.5"]

View File

@@ -4,6 +4,7 @@
"codeowners": [],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/streamlabswater",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["streamlabswater"],
"requirements": ["streamlabswater==1.0.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@G-Two"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/subaru",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["stdiomask", "subarulink"],
"requirements": ["subarulink==0.7.15"]

View File

@@ -5,6 +5,7 @@
"codeowners": ["@ooii", "@jb101010-2"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/suez_water",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pysuez", "regex"],
"quality_scale": "bronze",

View File

@@ -9,6 +9,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/sunricher_dali",
"integration_type": "hub",
"iot_class": "local_push",
"quality_scale": "silver",
"requirements": ["PySrDaliGateway==0.19.3"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@benleb", "@danielhiversen"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/surepetcare",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["rich", "surepy"],
"requirements": ["surepy==0.9.0"]

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from typing import TYPE_CHECKING, Any
from swisshydrodata import SwissHydroData
import voluptuous as vol
@@ -67,8 +67,8 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Swiss hydrological sensor."""
station = config[CONF_STATION]
monitored_conditions = config[CONF_MONITORED_CONDITIONS]
station: int = config[CONF_STATION]
monitored_conditions: list[str] = config[CONF_MONITORED_CONDITIONS]
hydro_data = HydrologicalData(station)
hydro_data.update()
@@ -93,38 +93,24 @@ class SwissHydrologicalDataSensor(SensorEntity):
"Data provided by the Swiss Federal Office for the Environment FOEN"
)
def __init__(self, hydro_data, station, condition):
def __init__(
self, hydro_data: HydrologicalData, station: int, condition: str
) -> None:
"""Initialize the Swiss hydrological sensor."""
self.hydro_data = hydro_data
data = hydro_data.data
if TYPE_CHECKING:
# Setup will fail in setup_platform if the data is None.
assert data is not None
self._condition = condition
self._data = self._state = self._unit_of_measurement = None
self._icon = CONDITIONS[condition]
self._data: dict[str, Any] | None = data
self._attr_icon = CONDITIONS[condition]
self._attr_name = f"{data['water-body-name']} {condition}"
self._attr_native_unit_of_measurement = data["parameters"][condition]["unit"]
self._attr_unique_id = f"{station}_{condition}"
self._station = station
@property
def name(self):
"""Return the name of the sensor."""
return f"{self._data['water-body-name']} {self._condition}"
@property
def unique_id(self) -> str:
"""Return a unique, friendly identifier for this entity."""
return f"{self._station}_{self._condition}"
@property
def native_unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self._state is not None:
return self.hydro_data.data["parameters"][self._condition]["unit"]
return None
@property
def native_value(self):
"""Return the state of the sensor."""
if isinstance(self._state, (int, float)):
return round(self._state, 2)
return None
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
@@ -146,32 +132,28 @@ class SwissHydrologicalDataSensor(SensorEntity):
return attrs
@property
def icon(self):
"""Icon to use in the frontend."""
return self._icon
def update(self) -> None:
"""Get the latest data and update the state."""
self.hydro_data.update()
self._data = self.hydro_data.data
if self._data is None:
self._state = None
else:
self._state = self._data["parameters"][self._condition]["value"]
self._attr_native_value = None
if self._data is not None:
state = self._data["parameters"][self._condition]["value"]
if isinstance(state, (int, float)):
self._attr_native_value = round(state, 2)
class HydrologicalData:
"""The Class for handling the data retrieval."""
def __init__(self, station):
def __init__(self, station: int) -> None:
"""Initialize the data object."""
self.station = station
self.data = None
self.data: dict[str, Any] | None = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
def update(self) -> None:
"""Get the latest data."""
shd = SwissHydroData()

View File

@@ -4,6 +4,7 @@
"codeowners": ["@fabaff", "@miaucl"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/swiss_public_transport",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["opendata_transport"],
"requirements": ["python-opendata-transport==0.5.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@jafar-atili"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/switchbee",
"integration_type": "hub",
"iot_class": "local_push",
"requirements": ["pyswitchbee==1.8.3"]
}

View File

@@ -4,6 +4,7 @@
"codeowners": ["@thecode", "@YogevBokobza"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/switcher_kis",
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["aioswitcher"],
"quality_scale": "silver",

View File

@@ -4,6 +4,7 @@
"codeowners": ["@zhulik"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/syncthing",
"integration_type": "service",
"iot_class": "local_polling",
"loggers": ["aiosyncthing"],
"requirements": ["aiosyncthing==0.7.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@nielstron"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/syncthru",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["pysyncthru"],
"requirements": ["PySyncThru==0.8.0", "url-normalize==2.2.1"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Guy293"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tami4",
"integration_type": "device",
"iot_class": "cloud_polling",
"requirements": ["Tami4EdgeAPI==3.0"]
}

View File

@@ -468,7 +468,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse:
targets = _build_targets(service)
service_responses: JsonValueType = []
errors: list[tuple[HomeAssistantError, str]] = []
errors: list[tuple[Exception, str]] = []
# invoke the service for each target
for target_config_entry, target_chat_id, target_notify_entity_id in targets:
@@ -495,7 +495,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse:
assert isinstance(service_responses, list)
service_responses.extend(formatted_responses)
except HomeAssistantError as ex:
except (HomeAssistantError, TelegramError) as ex:
target = target_notify_entity_id or str(target_chat_id)
errors.append((ex, target))

View File

@@ -433,7 +433,6 @@ class TelegramNotificationService:
async def _send_msgs(
self,
func_send: Callable,
msg_error: str,
message_tag: str | None,
*args_msg: Any,
context: Context | None = None,
@@ -459,12 +458,10 @@ class TelegramNotificationService:
response: Message = await self._send_msg(
func_send,
msg_error,
message_tag,
chat_id,
*args_msg,
context=context,
suppress_error=len(chat_ids) > 1,
**kwargs_msg,
)
if response:
@@ -475,58 +472,39 @@ class TelegramNotificationService:
async def _send_msg(
self,
func_send: Callable,
msg_error: str,
message_tag: str | None,
*args_msg: Any,
context: Context | None = None,
suppress_error: bool = False,
**kwargs_msg: Any,
) -> Any:
"""Send one message."""
try:
out = await func_send(*args_msg, **kwargs_msg)
if isinstance(out, Message):
chat_id = out.chat_id
message_id = out.message_id
self._last_message_id[chat_id] = message_id
_LOGGER.debug(
"Last message ID: %s (from chat_id %s)",
self._last_message_id,
chat_id,
)
event_data: dict[str, Any] = {
ATTR_CHAT_ID: chat_id,
ATTR_MESSAGEID: message_id,
}
if message_tag is not None:
event_data[ATTR_MESSAGE_TAG] = message_tag
if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None:
event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[
ATTR_MESSAGE_THREAD_ID
]
event_data["bot"] = _get_bot_info(self.bot, self.config)
self.hass.bus.async_fire(
EVENT_TELEGRAM_SENT, event_data, context=context
)
async_dispatcher_send(
self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data
)
except TelegramError as exc:
if not suppress_error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="action_failed",
translation_placeholders={"error": str(exc)},
) from exc
_LOGGER.error(
"%s: %s. Args: %s, kwargs: %s", msg_error, exc, args_msg, kwargs_msg
out = await func_send(*args_msg, **kwargs_msg)
if isinstance(out, Message):
chat_id = out.chat_id
message_id = out.message_id
self._last_message_id[chat_id] = message_id
_LOGGER.debug(
"Last message ID: %s (from chat_id %s)",
self._last_message_id,
chat_id,
)
event_data: dict[str, Any] = {
ATTR_CHAT_ID: chat_id,
ATTR_MESSAGEID: message_id,
}
if message_tag is not None:
event_data[ATTR_MESSAGE_TAG] = message_tag
if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None:
event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[ATTR_MESSAGE_THREAD_ID]
event_data["bot"] = _get_bot_info(self.bot, self.config)
self.hass.bus.async_fire(EVENT_TELEGRAM_SENT, event_data, context=context)
async_dispatcher_send(
self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data
)
return None
return out
async def send_message(
@@ -542,7 +520,6 @@ class TelegramNotificationService:
params = self._get_msg_kwargs(kwargs)
return await self._send_msgs(
self.bot.send_message,
"Error sending message",
params[ATTR_MESSAGE_TAG],
text,
chat_id=chat_id,
@@ -567,7 +544,6 @@ class TelegramNotificationService:
_LOGGER.debug("Delete message %s in chat ID %s", message_id, chat_id)
deleted: bool = await self._send_msg(
self.bot.delete_message,
"Error deleting message",
None,
chat_id,
message_id,
@@ -644,7 +620,6 @@ class TelegramNotificationService:
return await self._send_msg(
self.bot.edit_message_media,
"Error editing message media",
params[ATTR_MESSAGE_TAG],
media=media,
chat_id=chat_id,
@@ -678,7 +653,6 @@ class TelegramNotificationService:
_LOGGER.debug("Editing message with ID %s", message_id or inline_message_id)
return await self._send_msg(
self.bot.edit_message_text,
"Error editing text message",
params[ATTR_MESSAGE_TAG],
text,
chat_id=chat_id,
@@ -693,7 +667,6 @@ class TelegramNotificationService:
if type_edit == SERVICE_EDIT_CAPTION:
return await self._send_msg(
self.bot.edit_message_caption,
"Error editing message attributes",
params[ATTR_MESSAGE_TAG],
chat_id=chat_id,
message_id=message_id,
@@ -707,7 +680,6 @@ class TelegramNotificationService:
return await self._send_msg(
self.bot.edit_message_reply_markup,
"Error editing message attributes",
params[ATTR_MESSAGE_TAG],
chat_id=chat_id,
message_id=message_id,
@@ -735,7 +707,6 @@ class TelegramNotificationService:
)
await self._send_msg(
self.bot.answer_callback_query,
"Error sending answer callback query",
params[ATTR_MESSAGE_TAG],
callback_query_id,
text=message,
@@ -756,7 +727,6 @@ class TelegramNotificationService:
_LOGGER.debug("Send action %s in chat ID %s", chat_action, chat_id)
is_successful = await self._send_msg(
self.bot.send_chat_action,
"Error sending action",
None,
chat_id=chat_id,
action=chat_action,
@@ -791,7 +761,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_PHOTO:
return await self._send_msgs(
self.bot.send_photo,
"Error sending photo",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
photo=file_content,
@@ -808,7 +777,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_STICKER:
return await self._send_msgs(
self.bot.send_sticker,
"Error sending sticker",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
sticker=file_content,
@@ -823,7 +791,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_VIDEO:
return await self._send_msgs(
self.bot.send_video,
"Error sending video",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
video=file_content,
@@ -840,7 +807,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_DOCUMENT:
return await self._send_msgs(
self.bot.send_document,
"Error sending document",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
document=file_content,
@@ -857,7 +823,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_VOICE:
return await self._send_msgs(
self.bot.send_voice,
"Error sending voice",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
voice=file_content,
@@ -873,7 +838,6 @@ class TelegramNotificationService:
# SERVICE_SEND_ANIMATION
return await self._send_msgs(
self.bot.send_animation,
"Error sending animation",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
animation=file_content,
@@ -899,7 +863,6 @@ class TelegramNotificationService:
if stickerid:
return await self._send_msgs(
self.bot.send_sticker,
"Error sending sticker",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
sticker=stickerid,
@@ -925,7 +888,6 @@ class TelegramNotificationService:
params = self._get_msg_kwargs(kwargs)
return await self._send_msgs(
self.bot.send_location,
"Error sending location",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
latitude=latitude,
@@ -951,7 +913,6 @@ class TelegramNotificationService:
openperiod = kwargs.get(ATTR_OPEN_PERIOD)
return await self._send_msgs(
self.bot.send_poll,
"Error sending poll",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
question=question,
@@ -974,9 +935,7 @@ class TelegramNotificationService:
) -> Any:
"""Remove bot from chat."""
_LOGGER.debug("Leave from chat ID %s", chat_id)
return await self._send_msg(
self.bot.leave_chat, "Error leaving chat", None, chat_id, context=context
)
return await self._send_msg(self.bot.leave_chat, None, chat_id, context=context)
async def set_message_reaction(
self,
@@ -1000,7 +959,6 @@ class TelegramNotificationService:
await self._send_msg(
self.bot.set_message_reaction,
"Error setting message reaction",
params[ATTR_MESSAGE_TAG],
chat_id,
message_id,
@@ -1023,7 +981,6 @@ class TelegramNotificationService:
directory_path = self.hass.config.path(DOMAIN)
file: File = await self._send_msg(
self.bot.get_file,
"Error getting file",
None,
file_id=file_id,
context=context,

View File

@@ -5,6 +5,7 @@
"config_flow": true,
"dependencies": ["http"],
"documentation": "https://www.home-assistant.io/integrations/telegram_bot",
"integration_type": "service",
"iot_class": "cloud_push",
"loggers": ["telegram"],
"quality_scale": "silver",

View File

@@ -4,6 +4,7 @@
"codeowners": ["@fredrike"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tellduslive",
"integration_type": "hub",
"iot_class": "cloud_polling",
"requirements": ["tellduslive==0.10.12"]
}

View File

@@ -18,6 +18,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/tesla_wall_connector",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["tesla_wall_connector"],
"requirements": ["tesla-wall-connector==1.1.0"]

View File

@@ -53,6 +53,7 @@
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermobeacon",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["thermobeacon-ble==0.10.0"]
}

View File

@@ -23,6 +23,7 @@
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermopro",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["thermopro-ble==1.1.3"]
}

View File

@@ -5,6 +5,7 @@
"config_flow": true,
"dependencies": ["application_credentials", "recorder"],
"documentation": "https://www.home-assistant.io/integrations/tibber",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["tibber"],
"requirements": ["pyTibber==0.35.0"]

View File

@@ -11,6 +11,7 @@
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/tilt_ble",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["tilt-ble==1.0.1"]
}

View File

@@ -4,6 +4,7 @@
"codeowners": ["@boralyl"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/todoist",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["todoist"],
"requirements": ["todoist-api-python==3.1.0"]

View File

@@ -12,6 +12,7 @@
"config_flow": true,
"dependencies": ["bluetooth"],
"documentation": "https://www.home-assistant.io/integrations/togrill",
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["togrill_bluetooth"],
"quality_scale": "bronze",

View File

@@ -9,6 +9,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/tolo",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["tololib"],
"requirements": ["tololib==1.2.2"]

View File

@@ -12,6 +12,7 @@
}
],
"documentation": "https://www.home-assistant.io/integrations/toon",
"integration_type": "device",
"iot_class": "cloud_push",
"loggers": ["toonapi"],
"requirements": ["toonapi==0.3.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@austinmroczek"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["total_connect_client"],
"requirements": ["total-connect-client==2025.12.2"]

View File

@@ -7,6 +7,7 @@
"homekit": {
"models": ["TRADFRI"]
},
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["pytradfri"],
"requirements": ["pytradfri[async]==9.0.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gjohansson-ST"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/trafikverket_camera",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pytrafikverket"],
"requirements": ["pytrafikverket==1.1.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gjohansson-ST"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/trafikverket_ferry",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pytrafikverket"],
"requirements": ["pytrafikverket==1.1.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gjohansson-ST"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/trafikverket_train",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pytrafikverket"],
"requirements": ["pytrafikverket==1.1.1"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gjohansson-ST"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/trafikverket_weatherstation",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pytrafikverket"],
"requirements": ["pytrafikverket==1.1.1"]

View File

@@ -5948,7 +5948,7 @@
"name": "Samsung Smart TV"
},
"syncthru": {
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling",
"name": "Samsung SyncThru Printer"
@@ -6411,7 +6411,7 @@
},
"snooz": {
"name": "Snooz",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push"
},
@@ -6440,7 +6440,7 @@
},
"solax": {
"name": "SolaX Power",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6463,7 +6463,7 @@
},
"sonarr": {
"name": "Sonarr",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6495,7 +6495,7 @@
"name": "Sony Projector"
},
"songpal": {
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push",
"name": "Sony Songpal"
@@ -6510,7 +6510,7 @@
},
"soundtouch": {
"name": "Bose SoundTouch",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6534,7 +6534,7 @@
},
"splunk": {
"name": "Splunk",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "local_push",
"single_config_entry": true
@@ -6553,7 +6553,7 @@
},
"srp_energy": {
"name": "SRP Energy",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
@@ -6571,7 +6571,7 @@
},
"starlink": {
"name": "Starlink",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6595,13 +6595,13 @@
},
"steamist": {
"name": "Steamist",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
"stiebel_eltron": {
"name": "STIEBEL ELTRON",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6613,7 +6613,7 @@
},
"streamlabswater": {
"name": "StreamLabs",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
@@ -6625,7 +6625,7 @@
},
"suez_water": {
"name": "Suez Water",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
@@ -6678,7 +6678,7 @@
},
"swiss_public_transport": {
"name": "Swiss public transport",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
@@ -6729,7 +6729,7 @@
},
"syncthing": {
"name": "Syncthing",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -6801,7 +6801,7 @@
},
"tami4": {
"name": "Tami4 Edge / Edge+",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "cloud_polling"
},
@@ -6869,7 +6869,7 @@
"name": "Telegram"
},
"telegram_bot": {
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_push",
"name": "Telegram bot"
@@ -6921,7 +6921,7 @@
"name": "Tesla Powerwall"
},
"tesla_wall_connector": {
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling",
"name": "Tesla Wall Connector"
@@ -6959,7 +6959,7 @@
},
"thermobeacon": {
"name": "ThermoBeacon",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push"
},
@@ -6970,7 +6970,7 @@
},
"thermopro": {
"name": "ThermoPro",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push"
},
@@ -7040,7 +7040,7 @@
"name": "Tilt",
"integrations": {
"tilt_ble": {
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push",
"name": "Tilt Hydrometer BLE"
@@ -7066,19 +7066,19 @@
},
"todoist": {
"name": "Todoist",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling"
},
"togrill": {
"name": "ToGrill",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push"
},
"tolo": {
"name": "TOLO Sauna",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_polling"
},
@@ -7096,7 +7096,7 @@
},
"toon": {
"name": "Toon",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "cloud_push"
},
@@ -7171,25 +7171,25 @@
"name": "Trafikverket",
"integrations": {
"trafikverket_camera": {
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling",
"name": "Trafikverket Camera"
},
"trafikverket_ferry": {
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling",
"name": "Trafikverket Ferry"
},
"trafikverket_train": {
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling",
"name": "Trafikverket Train"
},
"trafikverket_weatherstation": {
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling",
"name": "Trafikverket Weather Station"

View File

@@ -711,6 +711,7 @@ _ENTITY_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
function_name="icon",
return_type=["str", None],
mandatory=True,
),
TypeHintMatch(
function_name="entity_picture",

12
requirements_all.txt generated
View File

@@ -339,7 +339,7 @@ aionanoleaf2==1.0.2
aionotion==2024.03.0
# homeassistant.components.ntfy
aiontfy==0.7.0
aiontfy==0.8.0
# homeassistant.components.nut
aionut==4.3.4
@@ -1466,6 +1466,9 @@ lxml==6.0.1
# homeassistant.components.matrix
matrix-nio==0.25.2
# homeassistant.components.matter
matter-python-client==0.4.1
# homeassistant.components.maxcube
maxcube-api==0.4.3
@@ -2224,7 +2227,7 @@ pyliebherrhomeapi==0.3.0
pylitejet==0.6.3
# homeassistant.components.litterrobot
pylitterbot==2025.0.0
pylitterbot==2025.1.0
# homeassistant.components.lutron_caseta
pylutron-caseta==0.26.0
@@ -2364,7 +2367,7 @@ pyplaato==0.0.19
pypoint==3.0.0
# homeassistant.components.portainer
pyportainer==1.0.23
pyportainer==1.0.27
# homeassistant.components.probe_plus
pyprobeplus==1.1.2
@@ -2580,9 +2583,6 @@ python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay
python-linkplay==0.2.12
# homeassistant.components.matter
python-matter-server==8.1.2
# homeassistant.components.melcloud
python-melcloud==0.1.2

View File

@@ -324,7 +324,7 @@ aionanoleaf2==1.0.2
aionotion==2024.03.0
# homeassistant.components.ntfy
aiontfy==0.7.0
aiontfy==0.8.0
# homeassistant.components.nut
aionut==4.3.4
@@ -1282,6 +1282,9 @@ lxml==6.0.1
# homeassistant.components.matrix
matrix-nio==0.25.2
# homeassistant.components.matter
matter-python-client==0.4.1
# homeassistant.components.maxcube
maxcube-api==0.4.3
@@ -1895,7 +1898,7 @@ pyliebherrhomeapi==0.3.0
pylitejet==0.6.3
# homeassistant.components.litterrobot
pylitterbot==2025.0.0
pylitterbot==2025.1.0
# homeassistant.components.lutron_caseta
pylutron-caseta==0.26.0
@@ -2014,7 +2017,7 @@ pyplaato==0.0.19
pypoint==3.0.0
# homeassistant.components.portainer
pyportainer==1.0.23
pyportainer==1.0.27
# homeassistant.components.probe_plus
pyprobeplus==1.1.2
@@ -2176,9 +2179,6 @@ python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay
python-linkplay==0.2.12
# homeassistant.components.matter
python-matter-server==8.1.2
# homeassistant.components.melcloud
python-melcloud==0.1.2

View File

@@ -203,11 +203,6 @@ FORBIDDEN_PACKAGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
"sense": {"sense-energy": {"async-timeout"}},
"slimproto": {"aioslimproto": {"async-timeout"}},
"surepetcare": {"surepy": {"async-timeout"}},
"tami4": {
# https://github.com/SeleniumHQ/selenium/issues/16943
# tami4 > selenium > types*
"selenium": {"types-certifi", "types-urllib3"},
},
"travisci": {
# https://github.com/menegazzo/travispy seems to be unmaintained
# and unused https://www.home-assistant.io/integrations/travisci

View File

@@ -353,7 +353,7 @@ async def test_send_sticker_partial_error(
assert mock_send_sticker.call_count == 2
assert err.value.translation_key == "multiple_errors"
assert err.value.translation_placeholders == {
"errors": "`entity_id` notify.mock_title_mock_chat_1: Action failed. mock network error\n`entity_id` notify.mock_title_mock_chat_2: Action failed. mock network error"
"errors": "`entity_id` notify.mock_title_mock_chat_1: mock network error\n`entity_id` notify.mock_title_mock_chat_2: mock network error"
}
@@ -364,7 +364,7 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None:
) as mock_bot:
mock_bot.side_effect = NetworkError("mock network error")
with pytest.raises(HomeAssistantError) as err:
with pytest.raises(TelegramError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_SEND_STICKER,
@@ -377,8 +377,8 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None:
await hass.async_block_till_done()
mock_bot.assert_called_once()
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "action_failed"
assert err.typename == "NetworkError"
assert err.value.message == "mock network error"
async def test_send_message_with_invalid_inline_keyboard(
@@ -2264,7 +2264,7 @@ async def test_download_file_when_bot_failed_to_get_file(
"homeassistant.components.telegram_bot.bot.Bot.get_file",
AsyncMock(side_effect=TelegramError("failed to get file")),
),
pytest.raises(HomeAssistantError) as err,
pytest.raises(TelegramError) as err,
):
await hass.services.async_call(
DOMAIN,
@@ -2273,7 +2273,9 @@ async def test_download_file_when_bot_failed_to_get_file(
blocking=True,
)
await hass.async_block_till_done()
assert err.value.translation_key == "action_failed"
assert err.typename == "TelegramError"
assert err.value.message == "failed to get file"
async def test_download_file_when_empty_file_path(