mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
2024.10.2 (#128176)
This commit is contained in:
commit
6952d2420f
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -37,7 +37,7 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_VERSION: 10
|
CACHE_VERSION: 11
|
||||||
UV_CACHE_VERSION: 1
|
UV_CACHE_VERSION: 1
|
||||||
MYPY_CACHE_VERSION: 9
|
MYPY_CACHE_VERSION: 9
|
||||||
HA_SHORT_VERSION: "2024.10"
|
HA_SHORT_VERSION: "2024.10"
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/airgradient",
|
"documentation": "https://www.home-assistant.io/integrations/airgradient",
|
||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["airgradient==0.9.0"],
|
"requirements": ["airgradient==0.9.1"],
|
||||||
"zeroconf": ["_airgradient._tcp.local."]
|
"zeroconf": ["_airgradient._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
|
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["aioairzone_cloud"],
|
"loggers": ["aioairzone_cloud"],
|
||||||
"requirements": ["aioairzone-cloud==0.6.5"]
|
"requirements": ["aioairzone-cloud==0.6.6"]
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"create_entry": {
|
"create_entry": {
|
||||||
"default": "Successfully connected to AlarmDecoder."
|
"default": "Successfully connected to AlarmDecoder."
|
||||||
@ -37,7 +38,7 @@
|
|||||||
"title": "Configure AlarmDecoder",
|
"title": "Configure AlarmDecoder",
|
||||||
"description": "What would you like to edit?",
|
"description": "What would you like to edit?",
|
||||||
"data": {
|
"data": {
|
||||||
"edit_select": "Edit"
|
"edit_selection": "Edit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"arm_settings": {
|
"arm_settings": {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
"data": {
|
"data": {
|
||||||
"site_nmi": "Site NMI",
|
"site_id": "Site NMI",
|
||||||
"site_name": "Site Name"
|
"site_name": "Site Name"
|
||||||
},
|
},
|
||||||
"description": "Select the NMI of the site you would like to add"
|
"description": "Select the NMI of the site you would like to add"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"no_integration_selected": "You must select at least one integration to track"
|
"no_integrations_selected": "You must select at least one integration to track"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"no_integration_selected": "[%key:component::analytics_insights::config::error::no_integration_selected%]"
|
"no_integrations_selected": "[%key:component::analytics_insights::config::error::no_integrations_selected%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -4,6 +4,7 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .const import CONF_THRESHOLD, DEFAULT_THRESHOLD
|
||||||
from .coordinator import AuroraDataUpdateCoordinator
|
from .coordinator import AuroraDataUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
@ -21,9 +22,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: AuroraConfigEntry) -> bo
|
|||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def update_listener(hass: HomeAssistant, entry: AuroraConfigEntry) -> None:
|
||||||
|
"""Handle options update."""
|
||||||
|
entry.runtime_data.threshold = int(
|
||||||
|
entry.options.get(CONF_THRESHOLD, DEFAULT_THRESHOLD)
|
||||||
|
)
|
||||||
|
# refresh the state of the visibility alert binary sensor
|
||||||
|
await entry.runtime_data.async_request_refresh()
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: AuroraConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: AuroraConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
@ -38,8 +38,8 @@ class AuroraDataUpdateCoordinator(DataUpdateCoordinator[int]):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.api = AuroraForecast(async_get_clientsession(hass))
|
self.api = AuroraForecast(async_get_clientsession(hass))
|
||||||
self.latitude = int(self.config_entry.data[CONF_LATITUDE])
|
self.latitude = round(self.config_entry.data[CONF_LATITUDE])
|
||||||
self.longitude = int(self.config_entry.data[CONF_LONGITUDE])
|
self.longitude = round(self.config_entry.data[CONF_LONGITUDE])
|
||||||
self.threshold = int(
|
self.threshold = int(
|
||||||
self.config_entry.options.get(CONF_THRESHOLD, DEFAULT_THRESHOLD)
|
self.config_entry.options.get(CONF_THRESHOLD, DEFAULT_THRESHOLD)
|
||||||
)
|
)
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"data": {
|
"data": {
|
||||||
"threshold": "Threshold (%)"
|
"forecast_threshold": "Threshold (%)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"options": {
|
"init": {
|
||||||
"title": "Options for the Azure Event Hub.",
|
"title": "Options for the Azure Event Hub.",
|
||||||
"data": {
|
"data": {
|
||||||
"send_interval": "Interval between sending batches to the hub."
|
"send_interval": "Interval between sending batches to the hub."
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
"description": "Set up your BleBox to integrate with Home Assistant.",
|
"description": "Set up your BleBox to integrate with Home Assistant.",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "[%key:common::config_flow::data::ip%]",
|
"host": "[%key:common::config_flow::data::ip%]",
|
||||||
"port": "[%key:common::config_flow::data::port%]"
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
|
"port": "[%key:common::config_flow::data::port%]",
|
||||||
|
"username": "[%key:common::config_flow::data::username%]"
|
||||||
},
|
},
|
||||||
"title": "Set up your BleBox device"
|
"title": "Set up your BleBox device"
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/bluesound",
|
"documentation": "https://www.home-assistant.io/integrations/bluesound",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["pyblu==1.0.2"],
|
"requirements": ["pyblu==1.0.3"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_musc._tcp.local."
|
"type": "_musc._tcp.local."
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"reconfigure": {
|
"reconfigure_confirm": {
|
||||||
"data": {
|
"data": {
|
||||||
"filename": "[%key:component::bryant_evolution::config::step::user::data::filename%]"
|
"filename": "[%key:component::bryant_evolution::config::step::user::data::filename%]"
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
username=entry.data[CONF_USERNAME],
|
username=entry.data[CONF_USERNAME],
|
||||||
password=entry.data[CONF_PASSWORD],
|
password=entry.data[CONF_PASSWORD],
|
||||||
ssl_verify_cert=entry.data[CONF_VERIFY_SSL],
|
ssl_verify_cert=entry.data[CONF_VERIFY_SSL],
|
||||||
timeout=10,
|
timeout=30,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await hass.async_add_executor_job(client.principal)
|
await hass.async_add_executor_job(client.principal)
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aiostreammagic"],
|
"loggers": ["aiostreammagic"],
|
||||||
"requirements": ["aiostreammagic==2.3.1"],
|
"requirements": ["aiostreammagic==2.5.0"],
|
||||||
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
|
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,6 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/cast",
|
"documentation": "https://www.home-assistant.io/integrations/cast",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["casttube", "pychromecast"],
|
"loggers": ["casttube", "pychromecast"],
|
||||||
"requirements": ["PyChromecast==14.0.1"],
|
"requirements": ["PyChromecast==14.0.3"],
|
||||||
"zeroconf": ["_googlecast._tcp.local."]
|
"zeroconf": ["_googlecast._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["doorbirdpy"],
|
"loggers": ["doorbirdpy"],
|
||||||
"requirements": ["DoorBirdPy==3.0.2"],
|
"requirements": ["DoorBirdPy==3.0.4"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_axis-video._tcp.local.",
|
"type": "_axis-video._tcp.local.",
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
|
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
|
||||||
"quality_scale": "silver",
|
"quality_scale": "silver",
|
||||||
"requirements": ["pyDuotecno==2024.9.0"]
|
"requirements": ["pyDuotecno==2024.10.0"]
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,21 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"host": "[%key:common::config_flow::data::host%]",
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
"username": "[%key:common::config_flow::data::username%]",
|
"username": "[%key:common::config_flow::data::username%]",
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]",
|
||||||
|
"port": "[%key:common::config_flow::data::port%]"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"host": "The hostname or IP address of your Duotecno device."
|
"host": "The hostname or IP address of your Duotecno device."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"abort": {
|
||||||
|
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/econet",
|
"documentation": "https://www.home-assistant.io/integrations/econet",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["paho_mqtt", "pyeconet"],
|
"loggers": ["paho_mqtt", "pyeconet"],
|
||||||
"requirements": ["pyeconet==0.1.22"]
|
"requirements": ["pyeconet==0.1.23"]
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -20,5 +20,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["home-assistant-frontend==20241002.2"]
|
"requirements": ["home-assistant-frontend==20241002.3"]
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,5 @@ DOMAIN = "fujitsu_fglair"
|
|||||||
|
|
||||||
CONF_REGION = "region"
|
CONF_REGION = "region"
|
||||||
CONF_EUROPE = "is_europe"
|
CONF_EUROPE = "is_europe"
|
||||||
REGION_EU = "EU"
|
REGION_EU = "eu"
|
||||||
REGION_DEFAULT = "default"
|
REGION_DEFAULT = "default"
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/fujitsu_fglair",
|
"documentation": "https://www.home-assistant.io/integrations/fujitsu_fglair",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["ayla-iot-unofficial==1.4.1"]
|
"requirements": ["ayla-iot-unofficial==1.4.2"]
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["fyta_cli==0.6.6"]
|
"requirements": ["fyta_cli==0.6.7"]
|
||||||
}
|
}
|
||||||
|
@ -172,10 +172,12 @@ class BaseGoogleCloudProvider:
|
|||||||
_LOGGER.error("Error: %s when validating options: %s", err, options)
|
_LOGGER.error("Error: %s when validating options: %s", err, options)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
encoding = texttospeech.AudioEncoding(options[CONF_ENCODING])
|
encoding: texttospeech.AudioEncoding = texttospeech.AudioEncoding[
|
||||||
gender: texttospeech.SsmlVoiceGender | None = texttospeech.SsmlVoiceGender(
|
options[CONF_ENCODING]
|
||||||
|
] # type: ignore[misc]
|
||||||
|
gender: texttospeech.SsmlVoiceGender | None = texttospeech.SsmlVoiceGender[
|
||||||
options[CONF_GENDER]
|
options[CONF_GENDER]
|
||||||
)
|
] # type: ignore[misc]
|
||||||
voice = options[CONF_VOICE]
|
voice = options[CONF_VOICE]
|
||||||
if voice:
|
if voice:
|
||||||
gender = None
|
gender = None
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
||||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||||
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]"
|
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
},
|
},
|
||||||
"create_entry": {
|
"create_entry": {
|
||||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
||||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||||
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]"
|
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
},
|
},
|
||||||
"create_entry": {
|
"create_entry": {
|
||||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||||
|
@ -14,6 +14,9 @@ from homeassistant.util import dt as dt_util
|
|||||||
def next_due_date(task: dict[str, Any], last_cron: str) -> datetime.date | None:
|
def next_due_date(task: dict[str, Any], last_cron: str) -> datetime.date | None:
|
||||||
"""Calculate due date for dailies and yesterdailies."""
|
"""Calculate due date for dailies and yesterdailies."""
|
||||||
|
|
||||||
|
if task["everyX"] == 0 or not task.get("nextDue"): # grey dailies never become due
|
||||||
|
return None
|
||||||
|
|
||||||
today = to_date(last_cron)
|
today = to_date(last_cron)
|
||||||
startdate = to_date(task["startDate"])
|
startdate = to_date(task["startDate"])
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -24,11 +24,11 @@
|
|||||||
},
|
},
|
||||||
"cmd": {
|
"cmd": {
|
||||||
"name": "Command",
|
"name": "Command",
|
||||||
"description": "Command itself. Could be decimal number or string with hexadeximal notation: \"0x10\"."
|
"description": "Command itself. Could be decimal number or string with hexadecimal notation: \"0x10\"."
|
||||||
},
|
},
|
||||||
"dst": {
|
"dst": {
|
||||||
"name": "Destination",
|
"name": "Destination",
|
||||||
"description": "Destination for command. Could be decimal number or string with hexadeximal notation: \"0x10\"."
|
"description": "Destination for command. Could be decimal number or string with hexadecimal notation: \"0x10\"."
|
||||||
},
|
},
|
||||||
"raw": {
|
"raw": {
|
||||||
"name": "Raw",
|
"name": "Raw",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
},
|
},
|
||||||
"src": {
|
"src": {
|
||||||
"name": "Source",
|
"name": "Source",
|
||||||
"description": "Source of command. Could be decimal number or string with hexadeximal notation: \"0x10\"."
|
"description": "Source of command. Could be decimal number or string with hexadecimal notation: \"0x10\"."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["holidays==0.57", "babel==2.15.0"]
|
"requirements": ["holidays==0.58", "babel==2.15.0"]
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
HAP_SUFFIX = "._hap._tcp.local."
|
HAP_SUFFIX = "._hap._tcp.local."
|
||||||
POWERVIEW_G2_SUFFIX = "._powerview._tcp.local."
|
POWERVIEW_G2_SUFFIX = "._powerview._tcp.local."
|
||||||
POWERVIEW_G3_SUFFIX = "._powerview-g3._tcp.local."
|
POWERVIEW_G3_SUFFIX = "._PowerView-G3._tcp.local."
|
||||||
|
|
||||||
|
|
||||||
async def validate_input(hass: HomeAssistant, hub_address: str) -> dict[str, str]:
|
async def validate_input(hass: HomeAssistant, hub_address: str) -> dict[str, str]:
|
||||||
|
@ -19,5 +19,5 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["aiopvapi"],
|
"loggers": ["aiopvapi"],
|
||||||
"requirements": ["aiopvapi==3.1.1"],
|
"requirements": ["aiopvapi==3.1.1"],
|
||||||
"zeroconf": ["_powerview._tcp.local.", "_powerview-g3._tcp.local."]
|
"zeroconf": ["_powerview._tcp.local.", "_PowerView-G3._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,9 @@ class AutomowerBaseEntity(CoordinatorEntity[AutomowerDataUpdateCoordinator]):
|
|||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, mower_id)},
|
identifiers={(DOMAIN, mower_id)},
|
||||||
manufacturer="Husqvarna",
|
manufacturer="Husqvarna",
|
||||||
model=self.mower_attributes.system.model,
|
model=self.mower_attributes.system.model.removeprefix(
|
||||||
|
"HUSQVARNA "
|
||||||
|
).removeprefix("Husqvarna "),
|
||||||
name=self.mower_attributes.system.name,
|
name=self.mower_attributes.system.name,
|
||||||
serial_number=self.mower_attributes.system.serial_number,
|
serial_number=self.mower_attributes.system.serial_number,
|
||||||
suggested_area="Garden",
|
suggested_area="Garden",
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
|
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["aioautomower"],
|
"loggers": ["aioautomower"],
|
||||||
"requirements": ["aioautomower==2024.9.3"]
|
"requirements": ["aioautomower==2024.10.0"]
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ DEFAULT_WATERING_TIME = timedelta(minutes=15)
|
|||||||
|
|
||||||
MANUFACTURER = "Hydrawise"
|
MANUFACTURER = "Hydrawise"
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=60)
|
||||||
|
|
||||||
SIGNAL_UPDATE_HYDRAWISE = "hydrawise_update"
|
SIGNAL_UPDATE_HYDRAWISE = "hydrawise_update"
|
||||||
|
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/imgw_pib",
|
"documentation": "https://www.home-assistant.io/integrations/imgw_pib",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["imgw_pib==1.0.5"]
|
"requirements": ["imgw_pib==1.0.6"]
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
"invalid_host": "The host entry was not in full URL format, e.g., http://192.168.10.100:80"
|
"invalid_host": "The host entry was not in full URL format, e.g., http://192.168.10.100:80"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"xknx==3.2.0",
|
"xknx==3.2.0",
|
||||||
"xknxproject==3.8.0",
|
"xknxproject==3.8.1",
|
||||||
"knx-frontend==2024.9.10.221729"
|
"knx-frontend==2024.9.10.221729"
|
||||||
],
|
],
|
||||||
"single_config_entry": true
|
"single_config_entry": true
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/linkplay",
|
"documentation": "https://www.home-assistant.io/integrations/linkplay",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["python-linkplay==0.0.12"],
|
"loggers": ["linkplay"],
|
||||||
|
"requirements": ["python-linkplay==0.0.15"],
|
||||||
"zeroconf": ["_linkplay._tcp.local."]
|
"zeroconf": ["_linkplay._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
||||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||||
"wrong_account": "You can only reauthenticate this entry with the same microBees account."
|
"wrong_account": "You can only reauthenticate this entry with the same microBees account."
|
||||||
},
|
},
|
||||||
|
@ -14,5 +14,5 @@
|
|||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "assumed_state",
|
"iot_class": "assumed_state",
|
||||||
"loggers": ["motionblindsble"],
|
"loggers": ["motionblindsble"],
|
||||||
"requirements": ["motionblindsble==0.1.1"]
|
"requirements": ["motionblindsble==0.1.2"]
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class NYTGamesData:
|
|||||||
|
|
||||||
wordle: Wordle
|
wordle: Wordle
|
||||||
spelling_bee: SpellingBee | None
|
spelling_bee: SpellingBee | None
|
||||||
connections: Connections
|
connections: Connections | None
|
||||||
|
|
||||||
|
|
||||||
class NYTGamesCoordinator(DataUpdateCoordinator[NYTGamesData]):
|
class NYTGamesCoordinator(DataUpdateCoordinator[NYTGamesData]):
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/nyt_games",
|
"documentation": "https://www.home-assistant.io/integrations/nyt_games",
|
||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["nyt_games==0.4.2"]
|
"requirements": ["nyt_games==0.4.3"]
|
||||||
}
|
}
|
||||||
|
@ -161,10 +161,11 @@ async def async_setup_entry(
|
|||||||
NYTGamesSpellingBeeSensor(coordinator, description)
|
NYTGamesSpellingBeeSensor(coordinator, description)
|
||||||
for description in SPELLING_BEE_SENSORS
|
for description in SPELLING_BEE_SENSORS
|
||||||
)
|
)
|
||||||
entities.extend(
|
if coordinator.data.connections is not None:
|
||||||
NYTGamesConnectionsSensor(coordinator, description)
|
entities.extend(
|
||||||
for description in CONNECTIONS_SENSORS
|
NYTGamesConnectionsSensor(coordinator, description)
|
||||||
)
|
for description in CONNECTIONS_SENSORS
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
@ -236,4 +237,5 @@ class NYTGamesConnectionsSensor(ConnectionsEntity, SensorEntity):
|
|||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType | date:
|
def native_value(self) -> StateType | date:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
|
assert self.coordinator.data.connections is not None
|
||||||
return self.entity_description.value_fn(self.coordinator.data.connections)
|
return self.entity_description.value_fn(self.coordinator.data.connections)
|
||||||
|
@ -130,20 +130,32 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]):
|
|||||||
continue
|
continue
|
||||||
start = cost_reads[0].start_time
|
start = cost_reads[0].start_time
|
||||||
_LOGGER.debug("Getting statistics at: %s", start)
|
_LOGGER.debug("Getting statistics at: %s", start)
|
||||||
stats = await get_instance(self.hass).async_add_executor_job(
|
# In the common case there should be a previous statistic at start time
|
||||||
statistics_during_period,
|
# so we only need to fetch one statistic. If there isn't any, fetch all.
|
||||||
self.hass,
|
for end in (start + timedelta(seconds=1), None):
|
||||||
start,
|
stats = await get_instance(self.hass).async_add_executor_job(
|
||||||
start + timedelta(seconds=1),
|
statistics_during_period,
|
||||||
{cost_statistic_id, consumption_statistic_id},
|
self.hass,
|
||||||
"hour",
|
start,
|
||||||
None,
|
end,
|
||||||
{"sum"},
|
{cost_statistic_id, consumption_statistic_id},
|
||||||
)
|
"hour",
|
||||||
|
None,
|
||||||
|
{"sum"},
|
||||||
|
)
|
||||||
|
if stats:
|
||||||
|
break
|
||||||
|
if end:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Not found. Trying to find the oldest statistic after %s",
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
# We are in this code path only if get_last_statistics found a stat
|
||||||
|
# so statistics_during_period should also have found at least one.
|
||||||
|
assert stats
|
||||||
cost_sum = cast(float, stats[cost_statistic_id][0]["sum"])
|
cost_sum = cast(float, stats[cost_statistic_id][0]["sum"])
|
||||||
consumption_sum = cast(float, stats[consumption_statistic_id][0]["sum"])
|
consumption_sum = cast(float, stats[consumption_statistic_id][0]["sum"])
|
||||||
last_stats_time = stats[consumption_statistic_id][0]["start"]
|
last_stats_time = stats[consumption_statistic_id][0]["start"]
|
||||||
assert last_stats_time == start.timestamp()
|
|
||||||
|
|
||||||
cost_statistics = []
|
cost_statistics = []
|
||||||
consumption_statistics = []
|
consumption_statistics = []
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/opower",
|
"documentation": "https://www.home-assistant.io/integrations/opower",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["opower"],
|
"loggers": ["opower"],
|
||||||
"requirements": ["opower==0.8.0"]
|
"requirements": ["opower==0.8.3"]
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
"already_configured": "The Thread border router is already configured",
|
||||||
|
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
|
@ -32,7 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
client_session=async_get_clientsession(hass),
|
client_session=async_get_clientsession(hass),
|
||||||
)
|
)
|
||||||
|
|
||||||
if custom_account := entry.data.get(CONF_ACCOUNT) is not None:
|
if (custom_account := entry.data.get(CONF_ACCOUNT)) is not None:
|
||||||
client.custom_account_id = custom_account
|
client.custom_account_id = custom_account
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -49,7 +49,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
async def async_update_data() -> OVODailyUsage:
|
async def async_update_data() -> OVODailyUsage:
|
||||||
"""Fetch data from OVO Energy."""
|
"""Fetch data from OVO Energy."""
|
||||||
if custom_account := entry.data.get(CONF_ACCOUNT) is not None:
|
if (custom_account := entry.data.get(CONF_ACCOUNT)) is not None:
|
||||||
client.custom_account_id = custom_account
|
client.custom_account_id = custom_account
|
||||||
|
|
||||||
async with asyncio.timeout(10):
|
async with asyncio.timeout(10):
|
||||||
|
@ -46,7 +46,7 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
client_session=async_get_clientsession(self.hass),
|
client_session=async_get_clientsession(self.hass),
|
||||||
)
|
)
|
||||||
|
|
||||||
if custom_account := user_input.get(CONF_ACCOUNT) is not None:
|
if (custom_account := user_input.get(CONF_ACCOUNT)) is not None:
|
||||||
client.custom_account_id = custom_account
|
client.custom_account_id = custom_account
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -10,6 +10,7 @@ from aiopyarr import exceptions
|
|||||||
from aiopyarr.models.host_configuration import PyArrHostConfiguration
|
from aiopyarr.models.host_configuration import PyArrHostConfiguration
|
||||||
from aiopyarr.radarr_client import RadarrClient
|
from aiopyarr.radarr_client import RadarrClient
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
from yarl import URL
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL
|
from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL
|
||||||
@ -54,6 +55,12 @@ class RadarrConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
user_input = dict(self.entry.data) if self.entry else None
|
user_input = dict(self.entry.data) if self.entry else None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# aiopyarr defaults to the service port if one isn't given
|
||||||
|
# this is counter to standard practice where http = 80
|
||||||
|
# and https = 443.
|
||||||
|
url = URL(user_input[CONF_URL])
|
||||||
|
user_input[CONF_URL] = f"{url.scheme}://{url.host}:{url.port}{url.path}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if result := await validate_input(self.hass, user_input):
|
if result := await validate_input(self.hass, user_input):
|
||||||
user_input[CONF_API_KEY] = result[1]
|
user_input[CONF_API_KEY] = result[1]
|
||||||
|
@ -10,13 +10,9 @@ import uuid
|
|||||||
from ring_doorbell import Auth, Ring, RingDevices
|
from ring_doorbell import Auth, Ring, RingDevices
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import APPLICATION_NAME, CONF_TOKEN
|
from homeassistant.const import APPLICATION_NAME, CONF_DEVICE_ID, CONF_TOKEN
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
device_registry as dr,
|
|
||||||
entity_registry as er,
|
|
||||||
instance_id,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .const import CONF_LISTEN_CREDENTIALS, DOMAIN, PLATFORMS
|
from .const import CONF_LISTEN_CREDENTIALS, DOMAIN, PLATFORMS
|
||||||
@ -38,18 +34,12 @@ class RingData:
|
|||||||
type RingConfigEntry = ConfigEntry[RingData]
|
type RingConfigEntry = ConfigEntry[RingData]
|
||||||
|
|
||||||
|
|
||||||
async def get_auth_agent_id(hass: HomeAssistant) -> tuple[str, str]:
|
def get_auth_user_agent() -> str:
|
||||||
"""Return user-agent and hardware id for Auth instantiation.
|
"""Return user-agent for Auth instantiation.
|
||||||
|
|
||||||
user_agent will be the display name in the ring.com authorised devices.
|
user_agent will be the display name in the ring.com authorised devices.
|
||||||
hardware_id will uniquely describe the authorised HA device.
|
|
||||||
"""
|
"""
|
||||||
user_agent = f"{APPLICATION_NAME}/{DOMAIN}-integration"
|
return f"{APPLICATION_NAME}/{DOMAIN}-integration"
|
||||||
|
|
||||||
# Generate a new uuid from the instance_uuid to keep the HA one private
|
|
||||||
instance_uuid = uuid.UUID(hex=await instance_id.async_get(hass))
|
|
||||||
hardware_id = str(uuid.uuid5(instance_uuid, user_agent))
|
|
||||||
return user_agent, hardware_id
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: RingConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: RingConfigEntry) -> bool:
|
||||||
@ -69,13 +59,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: RingConfigEntry) -> bool
|
|||||||
data={**entry.data, CONF_LISTEN_CREDENTIALS: token},
|
data={**entry.data, CONF_LISTEN_CREDENTIALS: token},
|
||||||
)
|
)
|
||||||
|
|
||||||
user_agent, hardware_id = await get_auth_agent_id(hass)
|
user_agent = get_auth_user_agent()
|
||||||
client_session = async_get_clientsession(hass)
|
client_session = async_get_clientsession(hass)
|
||||||
auth = Auth(
|
auth = Auth(
|
||||||
user_agent,
|
user_agent,
|
||||||
entry.data[CONF_TOKEN],
|
entry.data[CONF_TOKEN],
|
||||||
token_updater,
|
token_updater,
|
||||||
hardware_id=hardware_id,
|
hardware_id=entry.data[CONF_DEVICE_ID],
|
||||||
http_client_session=client_session,
|
http_client_session=client_session,
|
||||||
)
|
)
|
||||||
ring = Ring(auth)
|
ring = Ring(auth)
|
||||||
@ -138,3 +128,25 @@ async def _migrate_old_unique_ids(hass: HomeAssistant, entry_id: str) -> None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
await er.async_migrate_entries(hass, entry_id, _async_migrator)
|
await er.async_migrate_entries(hass, entry_id, _async_migrator)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate old config entry."""
|
||||||
|
entry_version = entry.version
|
||||||
|
entry_minor_version = entry.minor_version
|
||||||
|
|
||||||
|
new_minor_version = 2
|
||||||
|
if entry_version == 1 and entry_minor_version == 1:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Migrating from version %s.%s", entry_version, entry_minor_version
|
||||||
|
)
|
||||||
|
hardware_id = str(uuid.uuid4())
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry,
|
||||||
|
data={**entry.data, CONF_DEVICE_ID: hardware_id},
|
||||||
|
minor_version=new_minor_version,
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Migration to version %s.%s complete", entry_version, new_minor_version
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
@ -3,18 +3,25 @@
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
import uuid
|
||||||
|
|
||||||
from ring_doorbell import Auth, AuthenticationError, Requires2FAError
|
from ring_doorbell import Auth, AuthenticationError, Requires2FAError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
|
from homeassistant.const import (
|
||||||
|
CONF_DEVICE_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_TOKEN,
|
||||||
|
CONF_USERNAME,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from . import get_auth_agent_id
|
from . import get_auth_user_agent
|
||||||
from .const import CONF_2FA, DOMAIN
|
from .const import CONF_2FA, CONF_CONFIG_ENTRY_MINOR_VERSION, DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -23,11 +30,15 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
STEP_REAUTH_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
|
STEP_REAUTH_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
|
||||||
|
|
||||||
|
STEP_RECONFIGURE_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
|
||||||
|
|
||||||
async def validate_input(hass: HomeAssistant, data: dict[str, str]) -> dict[str, Any]:
|
|
||||||
|
async def validate_input(
|
||||||
|
hass: HomeAssistant, hardware_id: str, data: dict[str, str]
|
||||||
|
) -> dict[str, Any]:
|
||||||
"""Validate the user input allows us to connect."""
|
"""Validate the user input allows us to connect."""
|
||||||
|
|
||||||
user_agent, hardware_id = await get_auth_agent_id(hass)
|
user_agent = get_auth_user_agent()
|
||||||
auth = Auth(
|
auth = Auth(
|
||||||
user_agent,
|
user_agent,
|
||||||
http_client_session=async_get_clientsession(hass),
|
http_client_session=async_get_clientsession(hass),
|
||||||
@ -52,8 +63,10 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle a config flow for Ring."""
|
"""Handle a config flow for Ring."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
MINOR_VERSION = CONF_CONFIG_ENTRY_MINOR_VERSION
|
||||||
|
|
||||||
user_pass: dict[str, Any] = {}
|
user_pass: dict[str, Any] = {}
|
||||||
|
hardware_id: str | None = None
|
||||||
reauth_entry: ConfigEntry | None = None
|
reauth_entry: ConfigEntry | None = None
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
@ -64,8 +77,10 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
if not self.hardware_id:
|
||||||
|
self.hardware_id = str(uuid.uuid4())
|
||||||
try:
|
try:
|
||||||
token = await validate_input(self.hass, user_input)
|
token = await validate_input(self.hass, self.hardware_id, user_input)
|
||||||
except Require2FA:
|
except Require2FA:
|
||||||
self.user_pass = user_input
|
self.user_pass = user_input
|
||||||
|
|
||||||
@ -78,7 +93,11 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
else:
|
else:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=user_input[CONF_USERNAME],
|
title=user_input[CONF_USERNAME],
|
||||||
data={CONF_USERNAME: user_input[CONF_USERNAME], CONF_TOKEN: token},
|
data={
|
||||||
|
CONF_DEVICE_ID: self.hardware_id,
|
||||||
|
CONF_USERNAME: user_input[CONF_USERNAME],
|
||||||
|
CONF_TOKEN: token,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -120,8 +139,13 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
if user_input:
|
if user_input:
|
||||||
user_input[CONF_USERNAME] = self.reauth_entry.data[CONF_USERNAME]
|
user_input[CONF_USERNAME] = self.reauth_entry.data[CONF_USERNAME]
|
||||||
|
# Reauth will use the same hardware id and re-authorise an existing
|
||||||
|
# authorised device.
|
||||||
|
if not self.hardware_id:
|
||||||
|
self.hardware_id = self.reauth_entry.data[CONF_DEVICE_ID]
|
||||||
|
assert self.hardware_id
|
||||||
try:
|
try:
|
||||||
token = await validate_input(self.hass, user_input)
|
token = await validate_input(self.hass, self.hardware_id, user_input)
|
||||||
except Require2FA:
|
except Require2FA:
|
||||||
self.user_pass = user_input
|
self.user_pass = user_input
|
||||||
return await self.async_step_2fa()
|
return await self.async_step_2fa()
|
||||||
@ -134,6 +158,7 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
data = {
|
data = {
|
||||||
CONF_USERNAME: user_input[CONF_USERNAME],
|
CONF_USERNAME: user_input[CONF_USERNAME],
|
||||||
CONF_TOKEN: token,
|
CONF_TOKEN: token,
|
||||||
|
CONF_DEVICE_ID: self.hardware_id,
|
||||||
}
|
}
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.reauth_entry, data=data
|
self.reauth_entry, data=data
|
||||||
@ -146,7 +171,8 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
data_schema=STEP_REAUTH_DATA_SCHEMA,
|
data_schema=STEP_REAUTH_DATA_SCHEMA,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
CONF_USERNAME: self.reauth_entry.data[CONF_USERNAME]
|
CONF_USERNAME: self.reauth_entry.data[CONF_USERNAME],
|
||||||
|
CONF_NAME: self.reauth_entry.data[CONF_USERNAME],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
|
|
||||||
@ -31,3 +32,5 @@ SCAN_INTERVAL = timedelta(minutes=1)
|
|||||||
|
|
||||||
CONF_2FA = "2fa"
|
CONF_2FA = "2fa"
|
||||||
CONF_LISTEN_CREDENTIALS = "listen_token"
|
CONF_LISTEN_CREDENTIALS = "listen_token"
|
||||||
|
|
||||||
|
CONF_CONFIG_ENTRY_MINOR_VERSION: Final = 2
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -90,13 +90,21 @@ class SchlageDataUpdateCoordinator(DataUpdateCoordinator[SchlageData]):
|
|||||||
devices = dr.async_entries_for_config_entry(
|
devices = dr.async_entries_for_config_entry(
|
||||||
device_registry, self.config_entry.entry_id
|
device_registry, self.config_entry.entry_id
|
||||||
)
|
)
|
||||||
previous_locks = {device.id for device in devices}
|
previous_locks = set()
|
||||||
|
previous_locks_by_lock_id = {}
|
||||||
|
for device in devices:
|
||||||
|
for domain, identifier in device.identifiers:
|
||||||
|
if domain == DOMAIN:
|
||||||
|
previous_locks.add(identifier)
|
||||||
|
previous_locks_by_lock_id[identifier] = device
|
||||||
|
continue
|
||||||
current_locks = set(self.data.locks.keys())
|
current_locks = set(self.data.locks.keys())
|
||||||
|
|
||||||
if removed_locks := previous_locks - current_locks:
|
if removed_locks := previous_locks - current_locks:
|
||||||
LOGGER.debug("Removed locks: %s", ", ".join(removed_locks))
|
LOGGER.debug("Removed locks: %s", ", ".join(removed_locks))
|
||||||
for device_id in removed_locks:
|
for lock_id in removed_locks:
|
||||||
device_registry.async_update_device(
|
device_registry.async_update_device(
|
||||||
device_id=device_id,
|
device_id=previous_locks_by_lock_id[lock_id].id,
|
||||||
remove_config_entry_id=self.config_entry.entry_id,
|
remove_config_entry_id=self.config_entry.entry_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ from collections import defaultdict
|
|||||||
from collections.abc import Callable, Iterable
|
from collections.abc import Callable, Iterable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import datetime
|
import datetime
|
||||||
from functools import partial
|
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
@ -39,6 +38,7 @@ from homeassistant.helpers.entity import entity_sources
|
|||||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||||
from homeassistant.loader import async_suggest_report_issue
|
from homeassistant.loader import async_suggest_report_issue
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
from homeassistant.util.async_ import run_callback_threadsafe
|
||||||
from homeassistant.util.enum import try_parse_enum
|
from homeassistant.util.enum import try_parse_enum
|
||||||
from homeassistant.util.hass_dict import HassKey
|
from homeassistant.util.hass_dict import HassKey
|
||||||
|
|
||||||
@ -686,7 +686,6 @@ def list_statistic_ids(
|
|||||||
@callback
|
@callback
|
||||||
def _update_issues(
|
def _update_issues(
|
||||||
report_issue: Callable[[str, str, dict[str, Any]], None],
|
report_issue: Callable[[str, str, dict[str, Any]], None],
|
||||||
clear_issue: Callable[[str, str], None],
|
|
||||||
sensor_states: list[State],
|
sensor_states: list[State],
|
||||||
metadatas: dict[str, tuple[int, StatisticMetaData]],
|
metadatas: dict[str, tuple[int, StatisticMetaData]],
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -707,8 +706,6 @@ def _update_issues(
|
|||||||
entity_id,
|
entity_id,
|
||||||
{"statistic_id": entity_id},
|
{"statistic_id": entity_id},
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
clear_issue("state_class_removed", entity_id)
|
|
||||||
|
|
||||||
metadata_unit = metadata[1]["unit_of_measurement"]
|
metadata_unit = metadata[1]["unit_of_measurement"]
|
||||||
converter = statistics.STATISTIC_UNIT_TO_UNIT_CONVERTER.get(metadata_unit)
|
converter = statistics.STATISTIC_UNIT_TO_UNIT_CONVERTER.get(metadata_unit)
|
||||||
@ -725,8 +722,6 @@ def _update_issues(
|
|||||||
"supported_unit": metadata_unit,
|
"supported_unit": metadata_unit,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
clear_issue("units_changed", entity_id)
|
|
||||||
elif numeric and state_unit not in converter.VALID_UNITS:
|
elif numeric and state_unit not in converter.VALID_UNITS:
|
||||||
# The state unit can't be converted to the unit in metadata
|
# The state unit can't be converted to the unit in metadata
|
||||||
valid_units = (unit or "<None>" for unit in converter.VALID_UNITS)
|
valid_units = (unit or "<None>" for unit in converter.VALID_UNITS)
|
||||||
@ -741,8 +736,6 @@ def _update_issues(
|
|||||||
"supported_unit": valid_units_str,
|
"supported_unit": valid_units_str,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
clear_issue("units_changed", entity_id)
|
|
||||||
|
|
||||||
|
|
||||||
def update_statistics_issues(
|
def update_statistics_issues(
|
||||||
@ -756,36 +749,50 @@ def update_statistics_issues(
|
|||||||
instance, session, statistic_source=RECORDER_DOMAIN
|
instance, session, statistic_source=RECORDER_DOMAIN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get_sensor_statistics_issues(hass: HomeAssistant) -> set[str]:
|
||||||
|
"""Return a list of statistics issues."""
|
||||||
|
issues = set()
|
||||||
|
issue_registry = ir.async_get(hass)
|
||||||
|
for issue in issue_registry.issues.values():
|
||||||
|
if (
|
||||||
|
issue.domain != DOMAIN
|
||||||
|
or not (issue_data := issue.data)
|
||||||
|
or issue_data.get("issue_type")
|
||||||
|
not in ("state_class_removed", "units_changed")
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
issues.add(issue.issue_id)
|
||||||
|
return issues
|
||||||
|
|
||||||
|
issues = run_callback_threadsafe(
|
||||||
|
hass.loop, get_sensor_statistics_issues, hass
|
||||||
|
).result()
|
||||||
|
|
||||||
def create_issue_registry_issue(
|
def create_issue_registry_issue(
|
||||||
issue_type: str, statistic_id: str, data: dict[str, Any]
|
issue_type: str, statistic_id: str, data: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create an issue registry issue."""
|
"""Create an issue registry issue."""
|
||||||
hass.loop.call_soon_threadsafe(
|
issue_id = f"{issue_type}_{statistic_id}"
|
||||||
partial(
|
issues.discard(issue_id)
|
||||||
ir.async_create_issue,
|
ir.create_issue(
|
||||||
hass,
|
hass,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
f"{issue_type}_{statistic_id}",
|
issue_id,
|
||||||
data=data | {"issue_type": issue_type},
|
data=data | {"issue_type": issue_type},
|
||||||
is_fixable=False,
|
is_fixable=False,
|
||||||
severity=ir.IssueSeverity.WARNING,
|
severity=ir.IssueSeverity.WARNING,
|
||||||
translation_key=issue_type,
|
translation_key=issue_type,
|
||||||
translation_placeholders=data,
|
translation_placeholders=data,
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete_issue_registry_issue(issue_type: str, statistic_id: str) -> None:
|
|
||||||
"""Delete an issue registry issue."""
|
|
||||||
hass.loop.call_soon_threadsafe(
|
|
||||||
ir.async_delete_issue, hass, DOMAIN, f"{issue_type}_{statistic_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_update_issues(
|
_update_issues(
|
||||||
create_issue_registry_issue,
|
create_issue_registry_issue,
|
||||||
delete_issue_registry_issue,
|
|
||||||
sensor_states,
|
sensor_states,
|
||||||
metadatas,
|
metadatas,
|
||||||
)
|
)
|
||||||
|
for issue_id in issues:
|
||||||
|
hass.loop.call_soon_threadsafe(ir.async_delete_issue, hass, DOMAIN, issue_id)
|
||||||
|
|
||||||
|
|
||||||
def validate_statistics(
|
def validate_statistics(
|
||||||
@ -811,7 +818,6 @@ def validate_statistics(
|
|||||||
|
|
||||||
_update_issues(
|
_update_issues(
|
||||||
create_statistic_validation_issue,
|
create_statistic_validation_issue,
|
||||||
lambda issue_type, statistic_id: None,
|
|
||||||
sensor_states,
|
sensor_states,
|
||||||
metadatas,
|
metadatas,
|
||||||
)
|
)
|
||||||
|
@ -135,3 +135,21 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
data[PYSMA_REMOVE_LISTENER]()
|
data[PYSMA_REMOVE_LISTENER]()
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate entry."""
|
||||||
|
|
||||||
|
_LOGGER.debug("Migrating from version %s", entry.version)
|
||||||
|
|
||||||
|
if entry.version == 1:
|
||||||
|
# 1 -> 2: Unique ID from integer to string
|
||||||
|
if entry.minor_version == 1:
|
||||||
|
minor_version = 2
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry, unique_id=str(entry.unique_id), minor_version=minor_version
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.debug("Migration successful")
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -40,6 +40,7 @@ class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle a config flow for SMA."""
|
"""Handle a config flow for SMA."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
MINOR_VERSION = 2
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
@ -76,7 +77,7 @@ class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
|
|
||||||
if not errors:
|
if not errors:
|
||||||
await self.async_set_unique_id(device_info["serial"])
|
await self.async_set_unique_id(str(device_info["serial"]))
|
||||||
self._abort_if_unique_id_configured(updates=self._data)
|
self._abort_if_unique_id_configured(updates=self._data)
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=self._data[CONF_HOST], data=self._data
|
title=self._data[CONF_HOST], data=self._data
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/smlight",
|
"documentation": "https://www.home-assistant.io/integrations/smlight",
|
||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"requirements": ["pysmlight==0.1.2"],
|
"requirements": ["pysmlight==0.1.3"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_slzb-06._tcp.local."
|
"type": "_slzb-06._tcp.local."
|
||||||
|
@ -38,7 +38,7 @@ class SolarLogCoordinatorEntity(SolarLogBaseEntity):
|
|||||||
"""Initialize the SolarLogCoordinator sensor."""
|
"""Initialize the SolarLogCoordinator sensor."""
|
||||||
super().__init__(coordinator, description)
|
super().__init__(coordinator, description)
|
||||||
|
|
||||||
self._attr_unique_id = f"{coordinator.unique_id}-{description.key}"
|
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
manufacturer="Solar-Log",
|
manufacturer="Solar-Log",
|
||||||
model="Controller",
|
model="Controller",
|
||||||
@ -59,8 +59,8 @@ class SolarLogInverterEntity(SolarLogBaseEntity):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the SolarLogInverter sensor."""
|
"""Initialize the SolarLogInverter sensor."""
|
||||||
super().__init__(coordinator, description)
|
super().__init__(coordinator, description)
|
||||||
name = f"{coordinator.unique_id}-{slugify(coordinator.solarlog.device_name(device_id))}"
|
name = f"{coordinator.unique_id}_{slugify(coordinator.solarlog.device_name(device_id))}"
|
||||||
self._attr_unique_id = f"{name}-{description.key}"
|
self._attr_unique_id = f"{name}_{description.key}"
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
manufacturer="Solar-Log",
|
manufacturer="Solar-Log",
|
||||||
model="Inverter",
|
model="Inverter",
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/solarlog",
|
"documentation": "https://www.home-assistant.io/integrations/solarlog",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["solarlog_cli"],
|
"loggers": ["solarlog_cli"],
|
||||||
"requirements": ["solarlog_cli==0.3.0"]
|
"requirements": ["solarlog_cli==0.3.1"]
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
"reconfigure_confirm": {
|
"reconfigure_confirm": {
|
||||||
"title": "Configure SolarLog",
|
"title": "Configure SolarLog",
|
||||||
"data": {
|
"data": {
|
||||||
"has_password": "[%key:component::solarlog::config::step::user::data::has_password%]"
|
"has_password": "[%key:component::solarlog::config::step::user::data::has_password%]",
|
||||||
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -27,12 +27,6 @@
|
|||||||
},
|
},
|
||||||
"call_query": {
|
"call_query": {
|
||||||
"service": "mdi:database"
|
"service": "mdi:database"
|
||||||
},
|
|
||||||
"sync": {
|
|
||||||
"service": "mdi:sync"
|
|
||||||
},
|
|
||||||
"unsync": {
|
|
||||||
"service": "mdi:sync-off"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,19 +30,3 @@ call_query:
|
|||||||
advanced: true
|
advanced: true
|
||||||
selector:
|
selector:
|
||||||
object:
|
object:
|
||||||
sync:
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: squeezebox
|
|
||||||
domain: media_player
|
|
||||||
fields:
|
|
||||||
other_player:
|
|
||||||
required: true
|
|
||||||
example: "media_player.living_room"
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
unsync:
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: squeezebox
|
|
||||||
domain: media_player
|
|
||||||
|
@ -60,20 +60,6 @@
|
|||||||
"description": "[%key:component::squeezebox::services::call_method::fields::parameters::description%]"
|
"description": "[%key:component::squeezebox::services::call_method::fields::parameters::description%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"sync": {
|
|
||||||
"name": "Sync",
|
|
||||||
"description": "Adds another player to this player's sync group. If the other player is already in a sync group, it will leave it.\n.",
|
|
||||||
"fields": {
|
|
||||||
"other_player": {
|
|
||||||
"name": "Other player",
|
|
||||||
"description": "Name of the other Squeezebox player to link."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unsync": {
|
|
||||||
"name": "Unsync",
|
|
||||||
"description": "Removes this player from its sync group."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
@ -37,17 +37,29 @@ class SensorData:
|
|||||||
|
|
||||||
def as_dict(self) -> dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
"""Return as dict."""
|
"""Return as dict."""
|
||||||
|
disk_usage = None
|
||||||
|
if self.disk_usage:
|
||||||
|
disk_usage = {k: str(v) for k, v in self.disk_usage.items()}
|
||||||
|
io_counters = None
|
||||||
|
if self.io_counters:
|
||||||
|
io_counters = {k: str(v) for k, v in self.io_counters.items()}
|
||||||
|
addresses = None
|
||||||
|
if self.addresses:
|
||||||
|
addresses = {k: str(v) for k, v in self.addresses.items()}
|
||||||
|
temperatures = None
|
||||||
|
if self.temperatures:
|
||||||
|
temperatures = {k: str(v) for k, v in self.temperatures.items()}
|
||||||
return {
|
return {
|
||||||
"disk_usage": {k: str(v) for k, v in self.disk_usage.items()},
|
"disk_usage": disk_usage,
|
||||||
"swap": str(self.swap),
|
"swap": str(self.swap),
|
||||||
"memory": str(self.memory),
|
"memory": str(self.memory),
|
||||||
"io_counters": {k: str(v) for k, v in self.io_counters.items()},
|
"io_counters": io_counters,
|
||||||
"addresses": {k: str(v) for k, v in self.addresses.items()},
|
"addresses": addresses,
|
||||||
"load": str(self.load),
|
"load": str(self.load),
|
||||||
"cpu_percent": str(self.cpu_percent),
|
"cpu_percent": str(self.cpu_percent),
|
||||||
"boot_time": str(self.boot_time),
|
"boot_time": str(self.boot_time),
|
||||||
"processes": str(self.processes),
|
"processes": str(self.processes),
|
||||||
"temperatures": {k: str(v) for k, v in self.temperatures.items()},
|
"temperatures": temperatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,8 +20,9 @@ from .models import TeslaFleetVehicleData
|
|||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
async def do_nothing() -> None:
|
async def do_nothing() -> dict[str, dict[str, bool]]:
|
||||||
"""Do nothing."""
|
"""Do nothing with a positive result."""
|
||||||
|
return {"response": {"result": True}}
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
@ -378,7 +378,17 @@ ENERGY_LIVE_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
|
|||||||
device_class=SensorDeviceClass.POWER,
|
device_class=SensorDeviceClass.POWER,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
SensorEntityDescription(key="island_status", device_class=SensorDeviceClass.ENUM),
|
SensorEntityDescription(
|
||||||
|
key="island_status",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
options=[
|
||||||
|
"on_grid",
|
||||||
|
"off_grid",
|
||||||
|
"off_grid_intentional",
|
||||||
|
"off_grid_unintentional",
|
||||||
|
"island_status_unknown",
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
WALL_CONNECTOR_DESCRIPTIONS: tuple[TeslemetrySensorEntityDescription, ...] = (
|
WALL_CONNECTOR_DESCRIPTIONS: tuple[TeslemetrySensorEntityDescription, ...] = (
|
||||||
|
@ -392,6 +392,16 @@
|
|||||||
"grid_services_power": {
|
"grid_services_power": {
|
||||||
"name": "Grid services power"
|
"name": "Grid services power"
|
||||||
},
|
},
|
||||||
|
"island_status": {
|
||||||
|
"name": "Island status",
|
||||||
|
"state": {
|
||||||
|
"island_status_unknown": "Unknown",
|
||||||
|
"on_grid": "On grid",
|
||||||
|
"off_grid": "Off grid",
|
||||||
|
"off_grid_intentional": "Off grid intentional",
|
||||||
|
"off_grid_unintentional": "Off grid unintentional"
|
||||||
|
}
|
||||||
|
},
|
||||||
"load_power": {
|
"load_power": {
|
||||||
"name": "Load power"
|
"name": "Load power"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_access_token": "[%key:common::config_flow::error::invalid_access_token%]",
|
"invalid_access_token": "[%key:common::config_flow::error::invalid_access_token%]",
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/touchline_sl",
|
"documentation": "https://www.home-assistant.io/integrations/touchline_sl",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["pytouchlinesl==0.1.7"]
|
"requirements": ["pytouchlinesl==0.1.8"]
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.components.climate import (
|
|||||||
HVACAction,
|
HVACAction,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.const import PRECISION_WHOLE
|
from homeassistant.const import PRECISION_TENTHS
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
@ -64,7 +64,7 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
|
|||||||
| ClimateEntityFeature.TURN_ON
|
| ClimateEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
||||||
_attr_precision = PRECISION_WHOLE
|
_attr_precision = PRECISION_TENTHS
|
||||||
|
|
||||||
# This disables the warning for async_turn_{on,off}, can be removed later.
|
# This disables the warning for async_turn_{on,off}, can be removed later.
|
||||||
_enable_turn_on_off_backwards_compatibility = False
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
@ -301,5 +301,5 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["kasa"],
|
"loggers": ["kasa"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["python-kasa[speedups]==0.7.4"]
|
"requirements": ["python-kasa[speedups]==0.7.5"]
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,17 @@ from homeassistant.helpers.entity import Entity
|
|||||||
from .const import DOMAIN, LOGGER, TUYA_HA_SIGNAL_UPDATE_ENTITY, DPCode, DPType
|
from .const import DOMAIN, LOGGER, TUYA_HA_SIGNAL_UPDATE_ENTITY, DPCode, DPType
|
||||||
from .util import remap_value
|
from .util import remap_value
|
||||||
|
|
||||||
|
_DPTYPE_MAPPING: dict[str, DPType] = {
|
||||||
|
"Bitmap": DPType.RAW,
|
||||||
|
"bitmap": DPType.RAW,
|
||||||
|
"bool": DPType.BOOLEAN,
|
||||||
|
"enum": DPType.ENUM,
|
||||||
|
"json": DPType.JSON,
|
||||||
|
"raw": DPType.RAW,
|
||||||
|
"string": DPType.STRING,
|
||||||
|
"value": DPType.INTEGER,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IntegerTypeData:
|
class IntegerTypeData:
|
||||||
@ -256,7 +267,13 @@ class TuyaEntity(Entity):
|
|||||||
order = ["function", "status_range"]
|
order = ["function", "status_range"]
|
||||||
for key in order:
|
for key in order:
|
||||||
if dpcode in getattr(self.device, key):
|
if dpcode in getattr(self.device, key):
|
||||||
return DPType(getattr(self.device, key)[dpcode].type)
|
current_type = getattr(self.device, key)[dpcode].type
|
||||||
|
try:
|
||||||
|
return DPType(current_type)
|
||||||
|
except ValueError:
|
||||||
|
# Sometimes, we get ill-formed DPTypes from the cloud,
|
||||||
|
# this fixes them and maps them to the correct DPType.
|
||||||
|
return _DPTYPE_MAPPING.get(current_type)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -42,7 +42,8 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"discovery_started": "Discovery started"
|
"discovery_started": "Discovery started",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
@ -34,11 +34,11 @@ class VenstarEntity(CoordinatorEntity[VenstarDataUpdateCoordinator]):
|
|||||||
@property
|
@property
|
||||||
def device_info(self) -> DeviceInfo:
|
def device_info(self) -> DeviceInfo:
|
||||||
"""Return the device information for this entity."""
|
"""Return the device information for this entity."""
|
||||||
fw_ver_major, fw_ver_minor = self._client.get_firmware_ver()
|
firmware_version = self._client.get_firmware_ver()
|
||||||
return DeviceInfo(
|
return DeviceInfo(
|
||||||
identifiers={(DOMAIN, self._config.entry_id)},
|
identifiers={(DOMAIN, self._config.entry_id)},
|
||||||
name=self._client.name,
|
name=self._client.name,
|
||||||
manufacturer="Venstar",
|
manufacturer="Venstar",
|
||||||
model=f"{self._client.model}-{self._client.get_type()}",
|
model=f"{self._client.model}-{self._client.get_type()}",
|
||||||
sw_version=f"{fw_ver_major}.{fw_ver_minor}",
|
sw_version=f"{firmware_version[0]}.{firmware_version[1]}",
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Types for the ViCare integration."""
|
"""Types for the ViCare integration."""
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import enum
|
import enum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -48,8 +49,12 @@ class HeatingProgram(enum.StrEnum):
|
|||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Return the mapped ViCare heating program for the Home Assistant preset."""
|
"""Return the mapped ViCare heating program for the Home Assistant preset."""
|
||||||
for program in supported_heating_programs:
|
for program in supported_heating_programs:
|
||||||
if VICARE_TO_HA_PRESET_HEATING.get(HeatingProgram(program)) == ha_preset:
|
with suppress(ValueError):
|
||||||
return program
|
if (
|
||||||
|
VICARE_TO_HA_PRESET_HEATING.get(HeatingProgram(program))
|
||||||
|
== ha_preset
|
||||||
|
):
|
||||||
|
return program
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
@ -802,7 +802,8 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
if not entities:
|
if not entities:
|
||||||
LOGGER.warning(
|
LOGGER.warning(
|
||||||
"No data found for Withings entry %s, sensors will be added when new data is available"
|
"No data found for Withings entry %s, sensors will be added when new data is available",
|
||||||
|
entry.title,
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
|
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
|
||||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||||
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]"
|
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
|
||||||
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
|
"wrong_account": "Authenticated account does not match the account to be reauthenticated. Please log in with the correct account."
|
||||||
},
|
},
|
||||||
"create_entry": {
|
"create_entry": {
|
||||||
"default": "Successfully authenticated with Withings."
|
"default": "Successfully authenticated with Withings."
|
||||||
|
@ -75,7 +75,7 @@ class WebControlProConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
if self.source == dhcp.DOMAIN:
|
if self.source == dhcp.DOMAIN:
|
||||||
discovery_info: DhcpServiceInfo = self.init_data
|
discovery_info: DhcpServiceInfo = self.init_data
|
||||||
data_values = {CONF_HOST: discovery_info.hostname or discovery_info.ip}
|
data_values = {CONF_HOST: discovery_info.ip}
|
||||||
else:
|
else:
|
||||||
data_values = {CONF_HOST: SUGGESTED_HOST}
|
data_values = {CONF_HOST: SUGGESTED_HOST}
|
||||||
|
|
||||||
|
@ -15,5 +15,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/wmspro",
|
"documentation": "https://www.home-assistant.io/integrations/wmspro",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["pywmspro==0.2.0"]
|
"requirements": ["pywmspro==0.2.1"]
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["holidays"],
|
"loggers": ["holidays"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["holidays==0.57"]
|
"requirements": ["holidays==0.58"]
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
"yxc_control_url_missing": "The control URL is not given in the ssdp description."
|
"yxc_control_url_missing": "The control URL is not given in the ssdp description."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"no_musiccast_device": "This device seems to be no MusicCast Device."
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"no_musiccast_device": "This device seems to be no MusicCast Device.",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
|
@ -34,6 +34,8 @@ def boolean(value: Any) -> bool:
|
|||||||
|
|
||||||
VALUE_SCHEMA = vol.Any(
|
VALUE_SCHEMA = vol.Any(
|
||||||
boolean,
|
boolean,
|
||||||
|
float,
|
||||||
|
int,
|
||||||
vol.Coerce(int),
|
vol.Coerce(int),
|
||||||
vol.Coerce(float),
|
vol.Coerce(float),
|
||||||
BITMASK_SCHEMA,
|
BITMASK_SCHEMA,
|
||||||
|
@ -24,7 +24,7 @@ if TYPE_CHECKING:
|
|||||||
APPLICATION_NAME: Final = "HomeAssistant"
|
APPLICATION_NAME: Final = "HomeAssistant"
|
||||||
MAJOR_VERSION: Final = 2024
|
MAJOR_VERSION: Final = 2024
|
||||||
MINOR_VERSION: Final = 10
|
MINOR_VERSION: Final = 10
|
||||||
PATCH_VERSION: Final = "1"
|
PATCH_VERSION: Final = "2"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||||
|
@ -267,6 +267,11 @@ HOMEKIT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ZEROCONF = {
|
ZEROCONF = {
|
||||||
|
"_PowerView-G3._tcp.local.": [
|
||||||
|
{
|
||||||
|
"domain": "hunterdouglas_powerview",
|
||||||
|
},
|
||||||
|
],
|
||||||
"_Volumio._tcp.local.": [
|
"_Volumio._tcp.local.": [
|
||||||
{
|
{
|
||||||
"domain": "volumio",
|
"domain": "volumio",
|
||||||
@ -695,11 +700,6 @@ ZEROCONF = {
|
|||||||
"domain": "plugwise",
|
"domain": "plugwise",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"_powerview-g3._tcp.local.": [
|
|
||||||
{
|
|
||||||
"domain": "hunterdouglas_powerview",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"_powerview._tcp.local.": [
|
"_powerview._tcp.local.": [
|
||||||
{
|
{
|
||||||
"domain": "hunterdouglas_powerview",
|
"domain": "hunterdouglas_powerview",
|
||||||
|
@ -9,6 +9,7 @@ import collections.abc
|
|||||||
from collections.abc import Callable, Generator, Iterable
|
from collections.abc import Callable, Generator, Iterable
|
||||||
from contextlib import AbstractContextManager
|
from contextlib import AbstractContextManager
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
from copy import deepcopy
|
||||||
from datetime import date, datetime, time, timedelta
|
from datetime import date, datetime, time, timedelta
|
||||||
from functools import cache, cached_property, lru_cache, partial, wraps
|
from functools import cache, cached_property, lru_cache, partial, wraps
|
||||||
import json
|
import json
|
||||||
@ -2166,7 +2167,8 @@ def merge_response(value: ServiceResponse) -> list[Any]:
|
|||||||
|
|
||||||
is_single_list = False
|
is_single_list = False
|
||||||
response_items: list = []
|
response_items: list = []
|
||||||
for entity_id, entity_response in value.items(): # pylint: disable=too-many-nested-blocks
|
input_service_response = deepcopy(value)
|
||||||
|
for entity_id, entity_response in input_service_response.items(): # pylint: disable=too-many-nested-blocks
|
||||||
if not isinstance(entity_response, dict):
|
if not isinstance(entity_response, dict):
|
||||||
raise TypeError("Response is not a dictionary")
|
raise TypeError("Response is not a dictionary")
|
||||||
for value_key, type_response in entity_response.items():
|
for value_key, type_response in entity_response.items():
|
||||||
|
@ -32,7 +32,7 @@ habluetooth==3.4.0
|
|||||||
hass-nabucasa==0.81.1
|
hass-nabucasa==0.81.1
|
||||||
hassil==1.7.4
|
hassil==1.7.4
|
||||||
home-assistant-bluetooth==1.12.2
|
home-assistant-bluetooth==1.12.2
|
||||||
home-assistant-frontend==20241002.2
|
home-assistant-frontend==20241002.3
|
||||||
home-assistant-intents==2024.10.2
|
home-assistant-intents==2024.10.2
|
||||||
httpx==0.27.2
|
httpx==0.27.2
|
||||||
ifaddr==0.2.0
|
ifaddr==0.2.0
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2024.10.1"
|
version = "2024.10.2"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
|
@ -13,7 +13,7 @@ AIOSomecomfort==0.0.25
|
|||||||
Adax-local==0.1.5
|
Adax-local==0.1.5
|
||||||
|
|
||||||
# homeassistant.components.doorbird
|
# homeassistant.components.doorbird
|
||||||
DoorBirdPy==3.0.2
|
DoorBirdPy==3.0.4
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
HAP-python==4.9.1
|
HAP-python==4.9.1
|
||||||
@ -45,7 +45,7 @@ ProgettiHWSW==0.1.3
|
|||||||
# PyBluez==0.22
|
# PyBluez==0.22
|
||||||
|
|
||||||
# homeassistant.components.cast
|
# homeassistant.components.cast
|
||||||
PyChromecast==14.0.1
|
PyChromecast==14.0.3
|
||||||
|
|
||||||
# homeassistant.components.flick_electric
|
# homeassistant.components.flick_electric
|
||||||
PyFlick==0.0.2
|
PyFlick==0.0.2
|
||||||
@ -176,7 +176,7 @@ aio-georss-gdacs==0.10
|
|||||||
aioairq==0.3.2
|
aioairq==0.3.2
|
||||||
|
|
||||||
# homeassistant.components.airzone_cloud
|
# homeassistant.components.airzone_cloud
|
||||||
aioairzone-cloud==0.6.5
|
aioairzone-cloud==0.6.6
|
||||||
|
|
||||||
# homeassistant.components.airzone
|
# homeassistant.components.airzone
|
||||||
aioairzone==0.9.3
|
aioairzone==0.9.3
|
||||||
@ -198,7 +198,7 @@ aioaseko==1.0.0
|
|||||||
aioasuswrt==1.4.0
|
aioasuswrt==1.4.0
|
||||||
|
|
||||||
# homeassistant.components.husqvarna_automower
|
# homeassistant.components.husqvarna_automower
|
||||||
aioautomower==2024.9.3
|
aioautomower==2024.10.0
|
||||||
|
|
||||||
# homeassistant.components.azure_devops
|
# homeassistant.components.azure_devops
|
||||||
aioazuredevops==2.2.1
|
aioazuredevops==2.2.1
|
||||||
@ -380,7 +380,7 @@ aiosolaredge==0.2.0
|
|||||||
aiosteamist==1.0.0
|
aiosteamist==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.cambridge_audio
|
# homeassistant.components.cambridge_audio
|
||||||
aiostreammagic==2.3.1
|
aiostreammagic==2.5.0
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==4.0.3
|
aioswitcher==4.0.3
|
||||||
@ -419,7 +419,7 @@ aiowithings==3.0.3
|
|||||||
aioymaps==1.2.5
|
aioymaps==1.2.5
|
||||||
|
|
||||||
# homeassistant.components.airgradient
|
# homeassistant.components.airgradient
|
||||||
airgradient==0.9.0
|
airgradient==0.9.1
|
||||||
|
|
||||||
# homeassistant.components.airly
|
# homeassistant.components.airly
|
||||||
airly==1.1.0
|
airly==1.1.0
|
||||||
@ -532,7 +532,7 @@ autarco==3.0.0
|
|||||||
axis==62
|
axis==62
|
||||||
|
|
||||||
# homeassistant.components.fujitsu_fglair
|
# homeassistant.components.fujitsu_fglair
|
||||||
ayla-iot-unofficial==1.4.1
|
ayla-iot-unofficial==1.4.2
|
||||||
|
|
||||||
# homeassistant.components.azure_event_hub
|
# homeassistant.components.azure_event_hub
|
||||||
azure-eventhub==5.11.1
|
azure-eventhub==5.11.1
|
||||||
@ -933,7 +933,7 @@ freesms==0.2.0
|
|||||||
fritzconnection[qr]==1.13.2
|
fritzconnection[qr]==1.13.2
|
||||||
|
|
||||||
# homeassistant.components.fyta
|
# homeassistant.components.fyta
|
||||||
fyta_cli==0.6.6
|
fyta_cli==0.6.7
|
||||||
|
|
||||||
# homeassistant.components.google_translate
|
# homeassistant.components.google_translate
|
||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
@ -1114,10 +1114,10 @@ hole==0.8.0
|
|||||||
|
|
||||||
# homeassistant.components.holiday
|
# homeassistant.components.holiday
|
||||||
# homeassistant.components.workday
|
# homeassistant.components.workday
|
||||||
holidays==0.57
|
holidays==0.58
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20241002.2
|
home-assistant-frontend==20241002.3
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.10.2
|
home-assistant-intents==2024.10.2
|
||||||
@ -1176,7 +1176,7 @@ iglo==1.2.7
|
|||||||
ihcsdk==2.8.5
|
ihcsdk==2.8.5
|
||||||
|
|
||||||
# homeassistant.components.imgw_pib
|
# homeassistant.components.imgw_pib
|
||||||
imgw_pib==1.0.5
|
imgw_pib==1.0.6
|
||||||
|
|
||||||
# homeassistant.components.incomfort
|
# homeassistant.components.incomfort
|
||||||
incomfort-client==0.6.3-1
|
incomfort-client==0.6.3-1
|
||||||
@ -1387,7 +1387,7 @@ mopeka-iot-ble==0.8.0
|
|||||||
motionblinds==0.6.25
|
motionblinds==0.6.25
|
||||||
|
|
||||||
# homeassistant.components.motionblinds_ble
|
# homeassistant.components.motionblinds_ble
|
||||||
motionblindsble==0.1.1
|
motionblindsble==0.1.2
|
||||||
|
|
||||||
# homeassistant.components.motioneye
|
# homeassistant.components.motioneye
|
||||||
motioneye-client==0.3.14
|
motioneye-client==0.3.14
|
||||||
@ -1484,7 +1484,7 @@ numato-gpio==0.13.0
|
|||||||
numpy==1.26.4
|
numpy==1.26.4
|
||||||
|
|
||||||
# homeassistant.components.nyt_games
|
# homeassistant.components.nyt_games
|
||||||
nyt_games==0.4.2
|
nyt_games==0.4.3
|
||||||
|
|
||||||
# homeassistant.components.oasa_telematics
|
# homeassistant.components.oasa_telematics
|
||||||
oasatelematics==0.3
|
oasatelematics==0.3
|
||||||
@ -1544,7 +1544,7 @@ openwrt-luci-rpc==1.1.17
|
|||||||
openwrt-ubus-rpc==0.0.2
|
openwrt-ubus-rpc==0.0.2
|
||||||
|
|
||||||
# homeassistant.components.opower
|
# homeassistant.components.opower
|
||||||
opower==0.8.0
|
opower==0.8.3
|
||||||
|
|
||||||
# homeassistant.components.oralb
|
# homeassistant.components.oralb
|
||||||
oralb-ble==0.17.6
|
oralb-ble==0.17.6
|
||||||
@ -1710,7 +1710,7 @@ pyCEC==0.5.2
|
|||||||
pyControl4==1.2.0
|
pyControl4==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.duotecno
|
# homeassistant.components.duotecno
|
||||||
pyDuotecno==2024.9.0
|
pyDuotecno==2024.10.0
|
||||||
|
|
||||||
# homeassistant.components.electrasmart
|
# homeassistant.components.electrasmart
|
||||||
pyElectra==1.2.4
|
pyElectra==1.2.4
|
||||||
@ -1780,7 +1780,7 @@ pybbox==0.0.5-alpha
|
|||||||
pyblackbird==0.6
|
pyblackbird==0.6
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
pyblu==1.0.2
|
pyblu==1.0.3
|
||||||
|
|
||||||
# homeassistant.components.neato
|
# homeassistant.components.neato
|
||||||
pybotvac==0.0.25
|
pybotvac==0.0.25
|
||||||
@ -1855,7 +1855,7 @@ pyebox==1.1.4
|
|||||||
pyecoforest==0.4.0
|
pyecoforest==0.4.0
|
||||||
|
|
||||||
# homeassistant.components.econet
|
# homeassistant.components.econet
|
||||||
pyeconet==0.1.22
|
pyeconet==0.1.23
|
||||||
|
|
||||||
# homeassistant.components.ista_ecotrend
|
# homeassistant.components.ista_ecotrend
|
||||||
pyecotrend-ista==3.3.1
|
pyecotrend-ista==3.3.1
|
||||||
@ -2244,7 +2244,7 @@ pysmarty2==0.10.1
|
|||||||
pysml==0.0.12
|
pysml==0.0.12
|
||||||
|
|
||||||
# homeassistant.components.smlight
|
# homeassistant.components.smlight
|
||||||
pysmlight==0.1.2
|
pysmlight==0.1.3
|
||||||
|
|
||||||
# homeassistant.components.snmp
|
# homeassistant.components.snmp
|
||||||
pysnmp==6.2.6
|
pysnmp==6.2.6
|
||||||
@ -2340,10 +2340,10 @@ python-join-api==0.0.9
|
|||||||
python-juicenet==1.1.0
|
python-juicenet==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.tplink
|
# homeassistant.components.tplink
|
||||||
python-kasa[speedups]==0.7.4
|
python-kasa[speedups]==0.7.5
|
||||||
|
|
||||||
# homeassistant.components.linkplay
|
# homeassistant.components.linkplay
|
||||||
python-linkplay==0.0.12
|
python-linkplay==0.0.15
|
||||||
|
|
||||||
# homeassistant.components.lirc
|
# homeassistant.components.lirc
|
||||||
# python-lirc==1.2.3
|
# python-lirc==1.2.3
|
||||||
@ -2413,7 +2413,7 @@ pytomorrowio==0.3.6
|
|||||||
pytouchline==0.7
|
pytouchline==0.7
|
||||||
|
|
||||||
# homeassistant.components.touchline_sl
|
# homeassistant.components.touchline_sl
|
||||||
pytouchlinesl==0.1.7
|
pytouchlinesl==0.1.8
|
||||||
|
|
||||||
# homeassistant.components.traccar
|
# homeassistant.components.traccar
|
||||||
# homeassistant.components.traccar_server
|
# homeassistant.components.traccar_server
|
||||||
@ -2477,7 +2477,7 @@ pywilight==0.0.74
|
|||||||
pywizlight==0.5.14
|
pywizlight==0.5.14
|
||||||
|
|
||||||
# homeassistant.components.wmspro
|
# homeassistant.components.wmspro
|
||||||
pywmspro==0.2.0
|
pywmspro==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.ws66i
|
# homeassistant.components.ws66i
|
||||||
pyws66i==1.1
|
pyws66i==1.1
|
||||||
@ -2676,7 +2676,7 @@ soco==0.30.4
|
|||||||
solaredge-local==0.2.3
|
solaredge-local==0.2.3
|
||||||
|
|
||||||
# homeassistant.components.solarlog
|
# homeassistant.components.solarlog
|
||||||
solarlog_cli==0.3.0
|
solarlog_cli==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.solax
|
# homeassistant.components.solax
|
||||||
solax==3.1.1
|
solax==3.1.1
|
||||||
@ -2992,7 +2992,7 @@ xiaomi-ble==0.32.0
|
|||||||
xknx==3.2.0
|
xknx==3.2.0
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknxproject==3.8.0
|
xknxproject==3.8.1
|
||||||
|
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
|
@ -13,7 +13,7 @@ AIOSomecomfort==0.0.25
|
|||||||
Adax-local==0.1.5
|
Adax-local==0.1.5
|
||||||
|
|
||||||
# homeassistant.components.doorbird
|
# homeassistant.components.doorbird
|
||||||
DoorBirdPy==3.0.2
|
DoorBirdPy==3.0.4
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
HAP-python==4.9.1
|
HAP-python==4.9.1
|
||||||
@ -42,7 +42,7 @@ PlexAPI==4.15.16
|
|||||||
ProgettiHWSW==0.1.3
|
ProgettiHWSW==0.1.3
|
||||||
|
|
||||||
# homeassistant.components.cast
|
# homeassistant.components.cast
|
||||||
PyChromecast==14.0.1
|
PyChromecast==14.0.3
|
||||||
|
|
||||||
# homeassistant.components.flick_electric
|
# homeassistant.components.flick_electric
|
||||||
PyFlick==0.0.2
|
PyFlick==0.0.2
|
||||||
@ -164,7 +164,7 @@ aio-georss-gdacs==0.10
|
|||||||
aioairq==0.3.2
|
aioairq==0.3.2
|
||||||
|
|
||||||
# homeassistant.components.airzone_cloud
|
# homeassistant.components.airzone_cloud
|
||||||
aioairzone-cloud==0.6.5
|
aioairzone-cloud==0.6.6
|
||||||
|
|
||||||
# homeassistant.components.airzone
|
# homeassistant.components.airzone
|
||||||
aioairzone==0.9.3
|
aioairzone==0.9.3
|
||||||
@ -186,7 +186,7 @@ aioaseko==1.0.0
|
|||||||
aioasuswrt==1.4.0
|
aioasuswrt==1.4.0
|
||||||
|
|
||||||
# homeassistant.components.husqvarna_automower
|
# homeassistant.components.husqvarna_automower
|
||||||
aioautomower==2024.9.3
|
aioautomower==2024.10.0
|
||||||
|
|
||||||
# homeassistant.components.azure_devops
|
# homeassistant.components.azure_devops
|
||||||
aioazuredevops==2.2.1
|
aioazuredevops==2.2.1
|
||||||
@ -362,7 +362,7 @@ aiosolaredge==0.2.0
|
|||||||
aiosteamist==1.0.0
|
aiosteamist==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.cambridge_audio
|
# homeassistant.components.cambridge_audio
|
||||||
aiostreammagic==2.3.1
|
aiostreammagic==2.5.0
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==4.0.3
|
aioswitcher==4.0.3
|
||||||
@ -401,7 +401,7 @@ aiowithings==3.0.3
|
|||||||
aioymaps==1.2.5
|
aioymaps==1.2.5
|
||||||
|
|
||||||
# homeassistant.components.airgradient
|
# homeassistant.components.airgradient
|
||||||
airgradient==0.9.0
|
airgradient==0.9.1
|
||||||
|
|
||||||
# homeassistant.components.airly
|
# homeassistant.components.airly
|
||||||
airly==1.1.0
|
airly==1.1.0
|
||||||
@ -481,7 +481,7 @@ autarco==3.0.0
|
|||||||
axis==62
|
axis==62
|
||||||
|
|
||||||
# homeassistant.components.fujitsu_fglair
|
# homeassistant.components.fujitsu_fglair
|
||||||
ayla-iot-unofficial==1.4.1
|
ayla-iot-unofficial==1.4.2
|
||||||
|
|
||||||
# homeassistant.components.azure_event_hub
|
# homeassistant.components.azure_event_hub
|
||||||
azure-eventhub==5.11.1
|
azure-eventhub==5.11.1
|
||||||
@ -786,7 +786,7 @@ freebox-api==1.1.0
|
|||||||
fritzconnection[qr]==1.13.2
|
fritzconnection[qr]==1.13.2
|
||||||
|
|
||||||
# homeassistant.components.fyta
|
# homeassistant.components.fyta
|
||||||
fyta_cli==0.6.6
|
fyta_cli==0.6.7
|
||||||
|
|
||||||
# homeassistant.components.google_translate
|
# homeassistant.components.google_translate
|
||||||
gTTS==2.2.4
|
gTTS==2.2.4
|
||||||
@ -940,10 +940,10 @@ hole==0.8.0
|
|||||||
|
|
||||||
# homeassistant.components.holiday
|
# homeassistant.components.holiday
|
||||||
# homeassistant.components.workday
|
# homeassistant.components.workday
|
||||||
holidays==0.57
|
holidays==0.58
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20241002.2
|
home-assistant-frontend==20241002.3
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.10.2
|
home-assistant-intents==2024.10.2
|
||||||
@ -987,7 +987,7 @@ idasen-ha==2.6.2
|
|||||||
ifaddr==0.2.0
|
ifaddr==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.imgw_pib
|
# homeassistant.components.imgw_pib
|
||||||
imgw_pib==1.0.5
|
imgw_pib==1.0.6
|
||||||
|
|
||||||
# homeassistant.components.incomfort
|
# homeassistant.components.incomfort
|
||||||
incomfort-client==0.6.3-1
|
incomfort-client==0.6.3-1
|
||||||
@ -1156,7 +1156,7 @@ mopeka-iot-ble==0.8.0
|
|||||||
motionblinds==0.6.25
|
motionblinds==0.6.25
|
||||||
|
|
||||||
# homeassistant.components.motionblinds_ble
|
# homeassistant.components.motionblinds_ble
|
||||||
motionblindsble==0.1.1
|
motionblindsble==0.1.2
|
||||||
|
|
||||||
# homeassistant.components.motioneye
|
# homeassistant.components.motioneye
|
||||||
motioneye-client==0.3.14
|
motioneye-client==0.3.14
|
||||||
@ -1232,7 +1232,7 @@ numato-gpio==0.13.0
|
|||||||
numpy==1.26.4
|
numpy==1.26.4
|
||||||
|
|
||||||
# homeassistant.components.nyt_games
|
# homeassistant.components.nyt_games
|
||||||
nyt_games==0.4.2
|
nyt_games==0.4.3
|
||||||
|
|
||||||
# homeassistant.components.google
|
# homeassistant.components.google
|
||||||
oauth2client==4.1.3
|
oauth2client==4.1.3
|
||||||
@ -1274,7 +1274,7 @@ openhomedevice==2.2.0
|
|||||||
openwebifpy==4.2.7
|
openwebifpy==4.2.7
|
||||||
|
|
||||||
# homeassistant.components.opower
|
# homeassistant.components.opower
|
||||||
opower==0.8.0
|
opower==0.8.3
|
||||||
|
|
||||||
# homeassistant.components.oralb
|
# homeassistant.components.oralb
|
||||||
oralb-ble==0.17.6
|
oralb-ble==0.17.6
|
||||||
@ -1396,7 +1396,7 @@ pyCEC==0.5.2
|
|||||||
pyControl4==1.2.0
|
pyControl4==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.duotecno
|
# homeassistant.components.duotecno
|
||||||
pyDuotecno==2024.9.0
|
pyDuotecno==2024.10.0
|
||||||
|
|
||||||
# homeassistant.components.electrasmart
|
# homeassistant.components.electrasmart
|
||||||
pyElectra==1.2.4
|
pyElectra==1.2.4
|
||||||
@ -1448,7 +1448,7 @@ pybalboa==1.0.2
|
|||||||
pyblackbird==0.6
|
pyblackbird==0.6
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
pyblu==1.0.2
|
pyblu==1.0.3
|
||||||
|
|
||||||
# homeassistant.components.neato
|
# homeassistant.components.neato
|
||||||
pybotvac==0.0.25
|
pybotvac==0.0.25
|
||||||
@ -1493,7 +1493,7 @@ pydroid-ipcam==2.0.0
|
|||||||
pyecoforest==0.4.0
|
pyecoforest==0.4.0
|
||||||
|
|
||||||
# homeassistant.components.econet
|
# homeassistant.components.econet
|
||||||
pyeconet==0.1.22
|
pyeconet==0.1.23
|
||||||
|
|
||||||
# homeassistant.components.ista_ecotrend
|
# homeassistant.components.ista_ecotrend
|
||||||
pyecotrend-ista==3.3.1
|
pyecotrend-ista==3.3.1
|
||||||
@ -1798,7 +1798,7 @@ pysmartthings==0.7.8
|
|||||||
pysml==0.0.12
|
pysml==0.0.12
|
||||||
|
|
||||||
# homeassistant.components.smlight
|
# homeassistant.components.smlight
|
||||||
pysmlight==0.1.2
|
pysmlight==0.1.3
|
||||||
|
|
||||||
# homeassistant.components.snmp
|
# homeassistant.components.snmp
|
||||||
pysnmp==6.2.6
|
pysnmp==6.2.6
|
||||||
@ -1861,10 +1861,10 @@ python-izone==1.2.9
|
|||||||
python-juicenet==1.1.0
|
python-juicenet==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.tplink
|
# homeassistant.components.tplink
|
||||||
python-kasa[speedups]==0.7.4
|
python-kasa[speedups]==0.7.5
|
||||||
|
|
||||||
# homeassistant.components.linkplay
|
# homeassistant.components.linkplay
|
||||||
python-linkplay==0.0.12
|
python-linkplay==0.0.15
|
||||||
|
|
||||||
# homeassistant.components.matter
|
# homeassistant.components.matter
|
||||||
python-matter-server==6.5.2
|
python-matter-server==6.5.2
|
||||||
@ -1919,7 +1919,7 @@ pytile==2023.12.0
|
|||||||
pytomorrowio==0.3.6
|
pytomorrowio==0.3.6
|
||||||
|
|
||||||
# homeassistant.components.touchline_sl
|
# homeassistant.components.touchline_sl
|
||||||
pytouchlinesl==0.1.7
|
pytouchlinesl==0.1.8
|
||||||
|
|
||||||
# homeassistant.components.traccar
|
# homeassistant.components.traccar
|
||||||
# homeassistant.components.traccar_server
|
# homeassistant.components.traccar_server
|
||||||
@ -1977,7 +1977,7 @@ pywilight==0.0.74
|
|||||||
pywizlight==0.5.14
|
pywizlight==0.5.14
|
||||||
|
|
||||||
# homeassistant.components.wmspro
|
# homeassistant.components.wmspro
|
||||||
pywmspro==0.2.0
|
pywmspro==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.ws66i
|
# homeassistant.components.ws66i
|
||||||
pyws66i==1.1
|
pyws66i==1.1
|
||||||
@ -2122,7 +2122,7 @@ snapcast==2.3.6
|
|||||||
soco==0.30.4
|
soco==0.30.4
|
||||||
|
|
||||||
# homeassistant.components.solarlog
|
# homeassistant.components.solarlog
|
||||||
solarlog_cli==0.3.0
|
solarlog_cli==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.solax
|
# homeassistant.components.solax
|
||||||
solax==3.1.1
|
solax==3.1.1
|
||||||
@ -2381,7 +2381,7 @@ xiaomi-ble==0.32.0
|
|||||||
xknx==3.2.0
|
xknx==3.2.0
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknxproject==3.8.0
|
xknxproject==3.8.1
|
||||||
|
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
# homeassistant.components.rest
|
# homeassistant.components.rest
|
||||||
|
@ -165,6 +165,8 @@ EXCEPTIONS = {
|
|||||||
"tapsaff", # https://github.com/bazwilliams/python-taps-aff/pull/5
|
"tapsaff", # https://github.com/bazwilliams/python-taps-aff/pull/5
|
||||||
"vincenty", # Public domain
|
"vincenty", # Public domain
|
||||||
"zeversolar", # https://github.com/kvanzuijlen/zeversolar/pull/46
|
"zeversolar", # https://github.com/kvanzuijlen/zeversolar/pull/46
|
||||||
|
# Using License-Expression (with hatchling)
|
||||||
|
"ftfy", # Apache-2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
TODO = {
|
TODO = {
|
||||||
|
@ -41,7 +41,7 @@ ZEROCONF_DISCOVERY_GEN3 = zeroconf.ZeroconfServiceInfo(
|
|||||||
ip_address="1.2.3.4",
|
ip_address="1.2.3.4",
|
||||||
ip_addresses=[IPv4Address("1.2.3.4")],
|
ip_addresses=[IPv4Address("1.2.3.4")],
|
||||||
hostname="mock_hostname",
|
hostname="mock_hostname",
|
||||||
name="Powerview Generation 3._powerview-g3._tcp.local.",
|
name="Powerview Generation 3._PowerView-G3._tcp.local.",
|
||||||
port=None,
|
port=None,
|
||||||
properties={},
|
properties={},
|
||||||
type="mock_type",
|
type="mock_type",
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
'labels': set({
|
'labels': set({
|
||||||
}),
|
}),
|
||||||
'manufacturer': 'Husqvarna',
|
'manufacturer': 'Husqvarna',
|
||||||
'model': 'HUSQVARNA AUTOMOWER® 450XH',
|
'model': 'AUTOMOWER® 450XH',
|
||||||
'model_id': None,
|
'model_id': None,
|
||||||
'name': 'Test Mower 1',
|
'name': 'Test Mower 1',
|
||||||
'name_by_user': None,
|
'name_by_user': None,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user