mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +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
|
||||
|
||||
env:
|
||||
CACHE_VERSION: 10
|
||||
CACHE_VERSION: 11
|
||||
UV_CACHE_VERSION: 1
|
||||
MYPY_CACHE_VERSION: 9
|
||||
HA_SHORT_VERSION: "2024.10"
|
||||
|
@ -6,6 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airgradient",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["airgradient==0.9.0"],
|
||||
"requirements": ["airgradient==0.9.1"],
|
||||
"zeroconf": ["_airgradient._tcp.local."]
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["aioairzone_cloud"],
|
||||
"requirements": ["aioairzone-cloud==0.6.5"]
|
||||
"requirements": ["aioairzone-cloud==0.6.6"]
|
||||
}
|
||||
|
@ -22,7 +22,8 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"default": "Successfully connected to AlarmDecoder."
|
||||
@ -37,7 +38,7 @@
|
||||
"title": "Configure AlarmDecoder",
|
||||
"description": "What would you like to edit?",
|
||||
"data": {
|
||||
"edit_select": "Edit"
|
||||
"edit_selection": "Edit"
|
||||
}
|
||||
},
|
||||
"arm_settings": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
},
|
||||
"site": {
|
||||
"data": {
|
||||
"site_nmi": "Site NMI",
|
||||
"site_id": "Site NMI",
|
||||
"site_name": "Site Name"
|
||||
},
|
||||
"description": "Select the NMI of the site you would like to add"
|
||||
|
@ -17,7 +17,7 @@
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"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": {
|
||||
@ -37,7 +37,7 @@
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"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": {
|
||||
|
@ -4,6 +4,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_THRESHOLD, DEFAULT_THRESHOLD
|
||||
from .coordinator import AuroraDataUpdateCoordinator
|
||||
|
||||
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)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
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:
|
||||
"""Unload a config entry."""
|
||||
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.latitude = int(self.config_entry.data[CONF_LATITUDE])
|
||||
self.longitude = int(self.config_entry.data[CONF_LONGITUDE])
|
||||
self.latitude = round(self.config_entry.data[CONF_LATITUDE])
|
||||
self.longitude = round(self.config_entry.data[CONF_LONGITUDE])
|
||||
self.threshold = int(
|
||||
self.config_entry.options.get(CONF_THRESHOLD, DEFAULT_THRESHOLD)
|
||||
)
|
||||
|
@ -14,14 +14,15 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"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": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"threshold": "Threshold (%)"
|
||||
"forecast_threshold": "Threshold (%)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"options": {
|
||||
"init": {
|
||||
"title": "Options for the Azure Event Hub.",
|
||||
"data": {
|
||||
"send_interval": "Interval between sending batches to the hub."
|
||||
|
@ -15,7 +15,9 @@
|
||||
"description": "Set up your BleBox to integrate with Home Assistant.",
|
||||
"data": {
|
||||
"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"
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/bluesound",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["pyblu==1.0.2"],
|
||||
"requirements": ["pyblu==1.0.3"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_musc._tcp.local."
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"reconfigure": {
|
||||
"reconfigure_confirm": {
|
||||
"data": {
|
||||
"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],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
ssl_verify_cert=entry.data[CONF_VERIFY_SSL],
|
||||
timeout=10,
|
||||
timeout=30,
|
||||
)
|
||||
try:
|
||||
await hass.async_add_executor_job(client.principal)
|
||||
|
@ -7,6 +7,6 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiostreammagic"],
|
||||
"requirements": ["aiostreammagic==2.3.1"],
|
||||
"requirements": ["aiostreammagic==2.5.0"],
|
||||
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
|
||||
}
|
||||
|
@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/cast",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["casttube", "pychromecast"],
|
||||
"requirements": ["PyChromecast==14.0.1"],
|
||||
"requirements": ["PyChromecast==14.0.3"],
|
||||
"zeroconf": ["_googlecast._tcp.local."]
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["doorbirdpy"],
|
||||
"requirements": ["DoorBirdPy==3.0.2"],
|
||||
"requirements": ["DoorBirdPy==3.0.4"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_axis-video._tcp.local.",
|
||||
|
@ -7,5 +7,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["pyDuotecno==2024.9.0"]
|
||||
"requirements": ["pyDuotecno==2024.10.0"]
|
||||
}
|
||||
|
@ -5,18 +5,21 @@
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"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": {
|
||||
"host": "The hostname or IP address of your Duotecno device."
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/econet",
|
||||
"iot_class": "cloud_push",
|
||||
"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%]"
|
||||
},
|
||||
"error": {
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
}
|
||||
},
|
||||
|
@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"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_EUROPE = "is_europe"
|
||||
REGION_EU = "EU"
|
||||
REGION_EU = "eu"
|
||||
REGION_DEFAULT = "default"
|
||||
|
@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/fujitsu_fglair",
|
||||
"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",
|
||||
"iot_class": "cloud_polling",
|
||||
"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)
|
||||
return None, None
|
||||
|
||||
encoding = texttospeech.AudioEncoding(options[CONF_ENCODING])
|
||||
gender: texttospeech.SsmlVoiceGender | None = texttospeech.SsmlVoiceGender(
|
||||
encoding: texttospeech.AudioEncoding = texttospeech.AudioEncoding[
|
||||
options[CONF_ENCODING]
|
||||
] # type: ignore[misc]
|
||||
gender: texttospeech.SsmlVoiceGender | None = texttospeech.SsmlVoiceGender[
|
||||
options[CONF_GENDER]
|
||||
)
|
||||
] # type: ignore[misc]
|
||||
voice = options[CONF_VOICE]
|
||||
if voice:
|
||||
gender = None
|
||||
|
@ -21,7 +21,8 @@
|
||||
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"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": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
|
@ -21,7 +21,8 @@
|
||||
"wrong_account": "Wrong account: Please authenticate with the right account.",
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"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": {
|
||||
"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:
|
||||
"""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)
|
||||
startdate = to_date(task["startDate"])
|
||||
if TYPE_CHECKING:
|
||||
|
@ -24,11 +24,11 @@
|
||||
},
|
||||
"cmd": {
|
||||
"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": {
|
||||
"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": {
|
||||
"name": "Raw",
|
||||
@ -36,7 +36,7 @@
|
||||
},
|
||||
"src": {
|
||||
"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,
|
||||
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
||||
"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."
|
||||
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]:
|
||||
|
@ -19,5 +19,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aiopvapi"],
|
||||
"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(
|
||||
identifiers={(DOMAIN, mower_id)},
|
||||
manufacturer="Husqvarna",
|
||||
model=self.mower_attributes.system.model,
|
||||
model=self.mower_attributes.system.model.removeprefix(
|
||||
"HUSQVARNA "
|
||||
).removeprefix("Husqvarna "),
|
||||
name=self.mower_attributes.system.name,
|
||||
serial_number=self.mower_attributes.system.serial_number,
|
||||
suggested_area="Garden",
|
||||
|
@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["aioautomower"],
|
||||
"requirements": ["aioautomower==2024.9.3"]
|
||||
"requirements": ["aioautomower==2024.10.0"]
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ DEFAULT_WATERING_TIME = timedelta(minutes=15)
|
||||
|
||||
MANUFACTURER = "Hydrawise"
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
SIGNAL_UPDATE_HYDRAWISE = "hydrawise_update"
|
||||
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/imgw_pib",
|
||||
"iot_class": "cloud_polling",
|
||||
"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"
|
||||
},
|
||||
"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": {
|
||||
|
@ -24,6 +24,7 @@
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"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%]"
|
||||
},
|
||||
"error": {
|
||||
|
@ -12,7 +12,7 @@
|
||||
"quality_scale": "platinum",
|
||||
"requirements": [
|
||||
"xknx==3.2.0",
|
||||
"xknxproject==3.8.0",
|
||||
"xknxproject==3.8.1",
|
||||
"knx-frontend==2024.9.10.221729"
|
||||
],
|
||||
"single_config_entry": true
|
||||
|
@ -6,6 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/linkplay",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["python-linkplay==0.0.12"],
|
||||
"loggers": ["linkplay"],
|
||||
"requirements": ["python-linkplay==0.0.15"],
|
||||
"zeroconf": ["_linkplay._tcp.local."]
|
||||
}
|
||||
|
@ -19,7 +19,8 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"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%]",
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
"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%]",
|
||||
"wrong_account": "You can only reauthenticate this entry with the same microBees account."
|
||||
},
|
||||
|
@ -14,5 +14,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "assumed_state",
|
||||
"loggers": ["motionblindsble"],
|
||||
"requirements": ["motionblindsble==0.1.1"]
|
||||
"requirements": ["motionblindsble==0.1.2"]
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class NYTGamesData:
|
||||
|
||||
wordle: Wordle
|
||||
spelling_bee: SpellingBee | None
|
||||
connections: Connections
|
||||
connections: Connections | None
|
||||
|
||||
|
||||
class NYTGamesCoordinator(DataUpdateCoordinator[NYTGamesData]):
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/nyt_games",
|
||||
"integration_type": "service",
|
||||
"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)
|
||||
for description in SPELLING_BEE_SENSORS
|
||||
)
|
||||
entities.extend(
|
||||
NYTGamesConnectionsSensor(coordinator, description)
|
||||
for description in CONNECTIONS_SENSORS
|
||||
)
|
||||
if coordinator.data.connections is not None:
|
||||
entities.extend(
|
||||
NYTGamesConnectionsSensor(coordinator, description)
|
||||
for description in CONNECTIONS_SENSORS
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@ -236,4 +237,5 @@ class NYTGamesConnectionsSensor(ConnectionsEntity, SensorEntity):
|
||||
@property
|
||||
def native_value(self) -> StateType | date:
|
||||
"""Return the state of the sensor."""
|
||||
assert self.coordinator.data.connections is not None
|
||||
return self.entity_description.value_fn(self.coordinator.data.connections)
|
||||
|
@ -130,20 +130,32 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]):
|
||||
continue
|
||||
start = cost_reads[0].start_time
|
||||
_LOGGER.debug("Getting statistics at: %s", start)
|
||||
stats = await get_instance(self.hass).async_add_executor_job(
|
||||
statistics_during_period,
|
||||
self.hass,
|
||||
start,
|
||||
start + timedelta(seconds=1),
|
||||
{cost_statistic_id, consumption_statistic_id},
|
||||
"hour",
|
||||
None,
|
||||
{"sum"},
|
||||
)
|
||||
# In the common case there should be a previous statistic at start time
|
||||
# so we only need to fetch one statistic. If there isn't any, fetch all.
|
||||
for end in (start + timedelta(seconds=1), None):
|
||||
stats = await get_instance(self.hass).async_add_executor_job(
|
||||
statistics_during_period,
|
||||
self.hass,
|
||||
start,
|
||||
end,
|
||||
{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"])
|
||||
consumption_sum = cast(float, stats[consumption_statistic_id][0]["sum"])
|
||||
last_stats_time = stats[consumption_statistic_id][0]["start"]
|
||||
assert last_stats_time == start.timestamp()
|
||||
|
||||
cost_statistics = []
|
||||
consumption_statistics = []
|
||||
|
@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/opower",
|
||||
"iot_class": "cloud_polling",
|
||||
"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%]"
|
||||
},
|
||||
"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": {
|
||||
|
@ -32,7 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
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
|
||||
|
||||
try:
|
||||
@ -49,7 +49,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_update_data() -> OVODailyUsage:
|
||||
"""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
|
||||
|
||||
async with asyncio.timeout(10):
|
||||
|
@ -46,7 +46,7 @@ class OVOEnergyFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
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
|
||||
|
||||
try:
|
||||
|
@ -10,6 +10,7 @@ from aiopyarr import exceptions
|
||||
from aiopyarr.models.host_configuration import PyArrHostConfiguration
|
||||
from aiopyarr.radarr_client import RadarrClient
|
||||
import voluptuous as vol
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
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
|
||||
|
||||
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:
|
||||
if result := await validate_input(self.hass, user_input):
|
||||
user_input[CONF_API_KEY] = result[1]
|
||||
|
@ -10,13 +10,9 @@ import uuid
|
||||
from ring_doorbell import Auth, Ring, RingDevices
|
||||
|
||||
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.helpers import (
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
instance_id,
|
||||
)
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_LISTEN_CREDENTIALS, DOMAIN, PLATFORMS
|
||||
@ -38,18 +34,12 @@ class RingData:
|
||||
type RingConfigEntry = ConfigEntry[RingData]
|
||||
|
||||
|
||||
async def get_auth_agent_id(hass: HomeAssistant) -> tuple[str, str]:
|
||||
"""Return user-agent and hardware id for Auth instantiation.
|
||||
def get_auth_user_agent() -> str:
|
||||
"""Return user-agent for Auth instantiation.
|
||||
|
||||
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"
|
||||
|
||||
# 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
|
||||
return f"{APPLICATION_NAME}/{DOMAIN}-integration"
|
||||
|
||||
|
||||
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},
|
||||
)
|
||||
|
||||
user_agent, hardware_id = await get_auth_agent_id(hass)
|
||||
user_agent = get_auth_user_agent()
|
||||
client_session = async_get_clientsession(hass)
|
||||
auth = Auth(
|
||||
user_agent,
|
||||
entry.data[CONF_TOKEN],
|
||||
token_updater,
|
||||
hardware_id=hardware_id,
|
||||
hardware_id=entry.data[CONF_DEVICE_ID],
|
||||
http_client_session=client_session,
|
||||
)
|
||||
ring = Ring(auth)
|
||||
@ -138,3 +128,25 @@ async def _migrate_old_unique_ids(hass: HomeAssistant, entry_id: str) -> None:
|
||||
return None
|
||||
|
||||
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
|
||||
import logging
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
from ring_doorbell import Auth, AuthenticationError, Requires2FAError
|
||||
import voluptuous as vol
|
||||
|
||||
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.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from . import get_auth_agent_id
|
||||
from .const import CONF_2FA, DOMAIN
|
||||
from . import get_auth_user_agent
|
||||
from .const import CONF_2FA, CONF_CONFIG_ENTRY_MINOR_VERSION, DOMAIN
|
||||
|
||||
_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_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."""
|
||||
|
||||
user_agent, hardware_id = await get_auth_agent_id(hass)
|
||||
user_agent = get_auth_user_agent()
|
||||
auth = Auth(
|
||||
user_agent,
|
||||
http_client_session=async_get_clientsession(hass),
|
||||
@ -52,8 +63,10 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Ring."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = CONF_CONFIG_ENTRY_MINOR_VERSION
|
||||
|
||||
user_pass: dict[str, Any] = {}
|
||||
hardware_id: str | None = None
|
||||
reauth_entry: ConfigEntry | None = None
|
||||
|
||||
async def async_step_user(
|
||||
@ -64,8 +77,10 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if user_input is not None:
|
||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
||||
self._abort_if_unique_id_configured()
|
||||
if not self.hardware_id:
|
||||
self.hardware_id = str(uuid.uuid4())
|
||||
try:
|
||||
token = await validate_input(self.hass, user_input)
|
||||
token = await validate_input(self.hass, self.hardware_id, user_input)
|
||||
except Require2FA:
|
||||
self.user_pass = user_input
|
||||
|
||||
@ -78,7 +93,11 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
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(
|
||||
@ -120,8 +139,13 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
if user_input:
|
||||
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:
|
||||
token = await validate_input(self.hass, user_input)
|
||||
token = await validate_input(self.hass, self.hardware_id, user_input)
|
||||
except Require2FA:
|
||||
self.user_pass = user_input
|
||||
return await self.async_step_2fa()
|
||||
@ -134,6 +158,7 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
data = {
|
||||
CONF_USERNAME: user_input[CONF_USERNAME],
|
||||
CONF_TOKEN: token,
|
||||
CONF_DEVICE_ID: self.hardware_id,
|
||||
}
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.reauth_entry, data=data
|
||||
@ -146,7 +171,8 @@ class RingConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
data_schema=STEP_REAUTH_DATA_SCHEMA,
|
||||
errors=errors,
|
||||
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 datetime import timedelta
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.const import Platform
|
||||
|
||||
@ -31,3 +32,5 @@ SCAN_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
CONF_2FA = "2fa"
|
||||
CONF_LISTEN_CREDENTIALS = "listen_token"
|
||||
|
||||
CONF_CONFIG_ENTRY_MINOR_VERSION: Final = 2
|
||||
|
@ -28,7 +28,8 @@
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"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": {
|
||||
|
@ -14,7 +14,8 @@
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"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": {
|
||||
|
@ -90,13 +90,21 @@ class SchlageDataUpdateCoordinator(DataUpdateCoordinator[SchlageData]):
|
||||
devices = dr.async_entries_for_config_entry(
|
||||
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())
|
||||
|
||||
if removed_locks := previous_locks - current_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_id=device_id,
|
||||
device_id=previous_locks_by_lock_id[lock_id].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 contextlib import suppress
|
||||
import datetime
|
||||
from functools import partial
|
||||
import itertools
|
||||
import logging
|
||||
import math
|
||||
@ -39,6 +38,7 @@ from homeassistant.helpers.entity import entity_sources
|
||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||
from homeassistant.loader import async_suggest_report_issue
|
||||
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.hass_dict import HassKey
|
||||
|
||||
@ -686,7 +686,6 @@ def list_statistic_ids(
|
||||
@callback
|
||||
def _update_issues(
|
||||
report_issue: Callable[[str, str, dict[str, Any]], None],
|
||||
clear_issue: Callable[[str, str], None],
|
||||
sensor_states: list[State],
|
||||
metadatas: dict[str, tuple[int, StatisticMetaData]],
|
||||
) -> None:
|
||||
@ -707,8 +706,6 @@ def _update_issues(
|
||||
entity_id,
|
||||
{"statistic_id": entity_id},
|
||||
)
|
||||
else:
|
||||
clear_issue("state_class_removed", entity_id)
|
||||
|
||||
metadata_unit = metadata[1]["unit_of_measurement"]
|
||||
converter = statistics.STATISTIC_UNIT_TO_UNIT_CONVERTER.get(metadata_unit)
|
||||
@ -725,8 +722,6 @@ def _update_issues(
|
||||
"supported_unit": metadata_unit,
|
||||
},
|
||||
)
|
||||
else:
|
||||
clear_issue("units_changed", entity_id)
|
||||
elif numeric and state_unit not in converter.VALID_UNITS:
|
||||
# The state unit can't be converted to the unit in metadata
|
||||
valid_units = (unit or "<None>" for unit in converter.VALID_UNITS)
|
||||
@ -741,8 +736,6 @@ def _update_issues(
|
||||
"supported_unit": valid_units_str,
|
||||
},
|
||||
)
|
||||
else:
|
||||
clear_issue("units_changed", entity_id)
|
||||
|
||||
|
||||
def update_statistics_issues(
|
||||
@ -756,36 +749,50 @@ def update_statistics_issues(
|
||||
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(
|
||||
issue_type: str, statistic_id: str, data: dict[str, Any]
|
||||
) -> None:
|
||||
"""Create an issue registry issue."""
|
||||
hass.loop.call_soon_threadsafe(
|
||||
partial(
|
||||
ir.async_create_issue,
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"{issue_type}_{statistic_id}",
|
||||
data=data | {"issue_type": issue_type},
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key=issue_type,
|
||||
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}"
|
||||
issue_id = f"{issue_type}_{statistic_id}"
|
||||
issues.discard(issue_id)
|
||||
ir.create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
issue_id,
|
||||
data=data | {"issue_type": issue_type},
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key=issue_type,
|
||||
translation_placeholders=data,
|
||||
)
|
||||
|
||||
_update_issues(
|
||||
create_issue_registry_issue,
|
||||
delete_issue_registry_issue,
|
||||
sensor_states,
|
||||
metadatas,
|
||||
)
|
||||
for issue_id in issues:
|
||||
hass.loop.call_soon_threadsafe(ir.async_delete_issue, hass, DOMAIN, issue_id)
|
||||
|
||||
|
||||
def validate_statistics(
|
||||
@ -811,7 +818,6 @@ def validate_statistics(
|
||||
|
||||
_update_issues(
|
||||
create_statistic_validation_issue,
|
||||
lambda issue_type, statistic_id: None,
|
||||
sensor_states,
|
||||
metadatas,
|
||||
)
|
||||
|
@ -135,3 +135,21 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
data[PYSMA_REMOVE_LISTENER]()
|
||||
|
||||
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."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 2
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize."""
|
||||
@ -76,7 +77,7 @@ class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors["base"] = "unknown"
|
||||
|
||||
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)
|
||||
return self.async_create_entry(
|
||||
title=self._data[CONF_HOST], data=self._data
|
||||
|
@ -6,7 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/smlight",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["pysmlight==0.1.2"],
|
||||
"requirements": ["pysmlight==0.1.3"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_slzb-06._tcp.local."
|
||||
|
@ -38,7 +38,7 @@ class SolarLogCoordinatorEntity(SolarLogBaseEntity):
|
||||
"""Initialize the SolarLogCoordinator sensor."""
|
||||
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(
|
||||
manufacturer="Solar-Log",
|
||||
model="Controller",
|
||||
@ -59,8 +59,8 @@ class SolarLogInverterEntity(SolarLogBaseEntity):
|
||||
) -> None:
|
||||
"""Initialize the SolarLogInverter sensor."""
|
||||
super().__init__(coordinator, description)
|
||||
name = f"{coordinator.unique_id}-{slugify(coordinator.solarlog.device_name(device_id))}"
|
||||
self._attr_unique_id = f"{name}-{description.key}"
|
||||
name = f"{coordinator.unique_id}_{slugify(coordinator.solarlog.device_name(device_id))}"
|
||||
self._attr_unique_id = f"{name}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
manufacturer="Solar-Log",
|
||||
model="Inverter",
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/solarlog",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["solarlog_cli"],
|
||||
"requirements": ["solarlog_cli==0.3.0"]
|
||||
"requirements": ["solarlog_cli==0.3.1"]
|
||||
}
|
||||
|
@ -32,7 +32,8 @@
|
||||
"reconfigure_confirm": {
|
||||
"title": "Configure SolarLog",
|
||||
"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": {
|
||||
"service": "mdi:database"
|
||||
},
|
||||
"sync": {
|
||||
"service": "mdi:sync"
|
||||
},
|
||||
"unsync": {
|
||||
"service": "mdi:sync-off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,19 +30,3 @@ call_query:
|
||||
advanced: true
|
||||
selector:
|
||||
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%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
|
@ -21,7 +21,8 @@
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"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": {
|
||||
|
@ -37,17 +37,29 @@ class SensorData:
|
||||
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
"""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 {
|
||||
"disk_usage": {k: str(v) for k, v in self.disk_usage.items()},
|
||||
"disk_usage": disk_usage,
|
||||
"swap": str(self.swap),
|
||||
"memory": str(self.memory),
|
||||
"io_counters": {k: str(v) for k, v in self.io_counters.items()},
|
||||
"addresses": {k: str(v) for k, v in self.addresses.items()},
|
||||
"io_counters": io_counters,
|
||||
"addresses": addresses,
|
||||
"load": str(self.load),
|
||||
"cpu_percent": str(self.cpu_percent),
|
||||
"boot_time": str(self.boot_time),
|
||||
"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
|
||||
|
||||
|
||||
async def do_nothing() -> None:
|
||||
"""Do nothing."""
|
||||
async def do_nothing() -> dict[str, dict[str, bool]]:
|
||||
"""Do nothing with a positive result."""
|
||||
return {"response": {"result": True}}
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
@ -378,7 +378,17 @@ ENERGY_LIVE_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
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, ...] = (
|
||||
|
@ -392,6 +392,16 @@
|
||||
"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": {
|
||||
"name": "Load power"
|
||||
},
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"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": {
|
||||
"invalid_access_token": "[%key:common::config_flow::error::invalid_access_token%]",
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/touchline_sl",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["pytouchlinesl==0.1.7"]
|
||||
"requirements": ["pytouchlinesl==0.1.8"]
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import PRECISION_WHOLE
|
||||
from homeassistant.const import PRECISION_TENTHS
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
@ -64,7 +64,7 @@ class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_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.
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
@ -301,5 +301,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["kasa"],
|
||||
"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 .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
|
||||
class IntegerTypeData:
|
||||
@ -256,7 +267,13 @@ class TuyaEntity(Entity):
|
||||
order = ["function", "status_range"]
|
||||
for key in order:
|
||||
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
|
||||
|
||||
|
@ -42,7 +42,8 @@
|
||||
},
|
||||
"abort": {
|
||||
"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": {
|
||||
|
@ -34,11 +34,11 @@ class VenstarEntity(CoordinatorEntity[VenstarDataUpdateCoordinator]):
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""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(
|
||||
identifiers={(DOMAIN, self._config.entry_id)},
|
||||
name=self._client.name,
|
||||
manufacturer="Venstar",
|
||||
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."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from contextlib import suppress
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
from typing import Any
|
||||
@ -48,8 +49,12 @@ class HeatingProgram(enum.StrEnum):
|
||||
) -> str | None:
|
||||
"""Return the mapped ViCare heating program for the Home Assistant preset."""
|
||||
for program in supported_heating_programs:
|
||||
if VICARE_TO_HA_PRESET_HEATING.get(HeatingProgram(program)) == ha_preset:
|
||||
return program
|
||||
with suppress(ValueError):
|
||||
if (
|
||||
VICARE_TO_HA_PRESET_HEATING.get(HeatingProgram(program))
|
||||
== ha_preset
|
||||
):
|
||||
return program
|
||||
return None
|
||||
|
||||
|
||||
|
@ -27,7 +27,8 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
|
@ -802,7 +802,8 @@ async def async_setup_entry(
|
||||
|
||||
if not entities:
|
||||
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)
|
||||
|
@ -20,7 +20,9 @@
|
||||
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"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": {
|
||||
"default": "Successfully authenticated with Withings."
|
||||
|
@ -75,7 +75,7 @@ class WebControlProConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
if self.source == dhcp.DOMAIN:
|
||||
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:
|
||||
data_values = {CONF_HOST: SUGGESTED_HOST}
|
||||
|
||||
|
@ -15,5 +15,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/wmspro",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["pywmspro==0.2.0"]
|
||||
"requirements": ["pywmspro==0.2.1"]
|
||||
}
|
||||
|
@ -7,5 +7,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["holidays"],
|
||||
"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."
|
||||
},
|
||||
"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": {
|
||||
|
@ -34,6 +34,8 @@ def boolean(value: Any) -> bool:
|
||||
|
||||
VALUE_SCHEMA = vol.Any(
|
||||
boolean,
|
||||
float,
|
||||
int,
|
||||
vol.Coerce(int),
|
||||
vol.Coerce(float),
|
||||
BITMASK_SCHEMA,
|
||||
|
@ -24,7 +24,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2024
|
||||
MINOR_VERSION: Final = 10
|
||||
PATCH_VERSION: Final = "1"
|
||||
PATCH_VERSION: Final = "2"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||
|
@ -267,6 +267,11 @@ HOMEKIT = {
|
||||
}
|
||||
|
||||
ZEROCONF = {
|
||||
"_PowerView-G3._tcp.local.": [
|
||||
{
|
||||
"domain": "hunterdouglas_powerview",
|
||||
},
|
||||
],
|
||||
"_Volumio._tcp.local.": [
|
||||
{
|
||||
"domain": "volumio",
|
||||
@ -695,11 +700,6 @@ ZEROCONF = {
|
||||
"domain": "plugwise",
|
||||
},
|
||||
],
|
||||
"_powerview-g3._tcp.local.": [
|
||||
{
|
||||
"domain": "hunterdouglas_powerview",
|
||||
},
|
||||
],
|
||||
"_powerview._tcp.local.": [
|
||||
{
|
||||
"domain": "hunterdouglas_powerview",
|
||||
|
@ -9,6 +9,7 @@ import collections.abc
|
||||
from collections.abc import Callable, Generator, Iterable
|
||||
from contextlib import AbstractContextManager
|
||||
from contextvars import ContextVar
|
||||
from copy import deepcopy
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from functools import cache, cached_property, lru_cache, partial, wraps
|
||||
import json
|
||||
@ -2166,7 +2167,8 @@ def merge_response(value: ServiceResponse) -> list[Any]:
|
||||
|
||||
is_single_list = False
|
||||
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):
|
||||
raise TypeError("Response is not a dictionary")
|
||||
for value_key, type_response in entity_response.items():
|
||||
|
@ -32,7 +32,7 @@ habluetooth==3.4.0
|
||||
hass-nabucasa==0.81.1
|
||||
hassil==1.7.4
|
||||
home-assistant-bluetooth==1.12.2
|
||||
home-assistant-frontend==20241002.2
|
||||
home-assistant-frontend==20241002.3
|
||||
home-assistant-intents==2024.10.2
|
||||
httpx==0.27.2
|
||||
ifaddr==0.2.0
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2024.10.1"
|
||||
version = "2024.10.2"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
@ -13,7 +13,7 @@ AIOSomecomfort==0.0.25
|
||||
Adax-local==0.1.5
|
||||
|
||||
# homeassistant.components.doorbird
|
||||
DoorBirdPy==3.0.2
|
||||
DoorBirdPy==3.0.4
|
||||
|
||||
# homeassistant.components.homekit
|
||||
HAP-python==4.9.1
|
||||
@ -45,7 +45,7 @@ ProgettiHWSW==0.1.3
|
||||
# PyBluez==0.22
|
||||
|
||||
# homeassistant.components.cast
|
||||
PyChromecast==14.0.1
|
||||
PyChromecast==14.0.3
|
||||
|
||||
# homeassistant.components.flick_electric
|
||||
PyFlick==0.0.2
|
||||
@ -176,7 +176,7 @@ aio-georss-gdacs==0.10
|
||||
aioairq==0.3.2
|
||||
|
||||
# homeassistant.components.airzone_cloud
|
||||
aioairzone-cloud==0.6.5
|
||||
aioairzone-cloud==0.6.6
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==0.9.3
|
||||
@ -198,7 +198,7 @@ aioaseko==1.0.0
|
||||
aioasuswrt==1.4.0
|
||||
|
||||
# homeassistant.components.husqvarna_automower
|
||||
aioautomower==2024.9.3
|
||||
aioautomower==2024.10.0
|
||||
|
||||
# homeassistant.components.azure_devops
|
||||
aioazuredevops==2.2.1
|
||||
@ -380,7 +380,7 @@ aiosolaredge==0.2.0
|
||||
aiosteamist==1.0.0
|
||||
|
||||
# homeassistant.components.cambridge_audio
|
||||
aiostreammagic==2.3.1
|
||||
aiostreammagic==2.5.0
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==4.0.3
|
||||
@ -419,7 +419,7 @@ aiowithings==3.0.3
|
||||
aioymaps==1.2.5
|
||||
|
||||
# homeassistant.components.airgradient
|
||||
airgradient==0.9.0
|
||||
airgradient==0.9.1
|
||||
|
||||
# homeassistant.components.airly
|
||||
airly==1.1.0
|
||||
@ -532,7 +532,7 @@ autarco==3.0.0
|
||||
axis==62
|
||||
|
||||
# homeassistant.components.fujitsu_fglair
|
||||
ayla-iot-unofficial==1.4.1
|
||||
ayla-iot-unofficial==1.4.2
|
||||
|
||||
# homeassistant.components.azure_event_hub
|
||||
azure-eventhub==5.11.1
|
||||
@ -933,7 +933,7 @@ freesms==0.2.0
|
||||
fritzconnection[qr]==1.13.2
|
||||
|
||||
# homeassistant.components.fyta
|
||||
fyta_cli==0.6.6
|
||||
fyta_cli==0.6.7
|
||||
|
||||
# homeassistant.components.google_translate
|
||||
gTTS==2.2.4
|
||||
@ -1114,10 +1114,10 @@ hole==0.8.0
|
||||
|
||||
# homeassistant.components.holiday
|
||||
# homeassistant.components.workday
|
||||
holidays==0.57
|
||||
holidays==0.58
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20241002.2
|
||||
home-assistant-frontend==20241002.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.10.2
|
||||
@ -1176,7 +1176,7 @@ iglo==1.2.7
|
||||
ihcsdk==2.8.5
|
||||
|
||||
# homeassistant.components.imgw_pib
|
||||
imgw_pib==1.0.5
|
||||
imgw_pib==1.0.6
|
||||
|
||||
# homeassistant.components.incomfort
|
||||
incomfort-client==0.6.3-1
|
||||
@ -1387,7 +1387,7 @@ mopeka-iot-ble==0.8.0
|
||||
motionblinds==0.6.25
|
||||
|
||||
# homeassistant.components.motionblinds_ble
|
||||
motionblindsble==0.1.1
|
||||
motionblindsble==0.1.2
|
||||
|
||||
# homeassistant.components.motioneye
|
||||
motioneye-client==0.3.14
|
||||
@ -1484,7 +1484,7 @@ numato-gpio==0.13.0
|
||||
numpy==1.26.4
|
||||
|
||||
# homeassistant.components.nyt_games
|
||||
nyt_games==0.4.2
|
||||
nyt_games==0.4.3
|
||||
|
||||
# homeassistant.components.oasa_telematics
|
||||
oasatelematics==0.3
|
||||
@ -1544,7 +1544,7 @@ openwrt-luci-rpc==1.1.17
|
||||
openwrt-ubus-rpc==0.0.2
|
||||
|
||||
# homeassistant.components.opower
|
||||
opower==0.8.0
|
||||
opower==0.8.3
|
||||
|
||||
# homeassistant.components.oralb
|
||||
oralb-ble==0.17.6
|
||||
@ -1710,7 +1710,7 @@ pyCEC==0.5.2
|
||||
pyControl4==1.2.0
|
||||
|
||||
# homeassistant.components.duotecno
|
||||
pyDuotecno==2024.9.0
|
||||
pyDuotecno==2024.10.0
|
||||
|
||||
# homeassistant.components.electrasmart
|
||||
pyElectra==1.2.4
|
||||
@ -1780,7 +1780,7 @@ pybbox==0.0.5-alpha
|
||||
pyblackbird==0.6
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
pyblu==1.0.2
|
||||
pyblu==1.0.3
|
||||
|
||||
# homeassistant.components.neato
|
||||
pybotvac==0.0.25
|
||||
@ -1855,7 +1855,7 @@ pyebox==1.1.4
|
||||
pyecoforest==0.4.0
|
||||
|
||||
# homeassistant.components.econet
|
||||
pyeconet==0.1.22
|
||||
pyeconet==0.1.23
|
||||
|
||||
# homeassistant.components.ista_ecotrend
|
||||
pyecotrend-ista==3.3.1
|
||||
@ -2244,7 +2244,7 @@ pysmarty2==0.10.1
|
||||
pysml==0.0.12
|
||||
|
||||
# homeassistant.components.smlight
|
||||
pysmlight==0.1.2
|
||||
pysmlight==0.1.3
|
||||
|
||||
# homeassistant.components.snmp
|
||||
pysnmp==6.2.6
|
||||
@ -2340,10 +2340,10 @@ python-join-api==0.0.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.7.4
|
||||
python-kasa[speedups]==0.7.5
|
||||
|
||||
# homeassistant.components.linkplay
|
||||
python-linkplay==0.0.12
|
||||
python-linkplay==0.0.15
|
||||
|
||||
# homeassistant.components.lirc
|
||||
# python-lirc==1.2.3
|
||||
@ -2413,7 +2413,7 @@ pytomorrowio==0.3.6
|
||||
pytouchline==0.7
|
||||
|
||||
# homeassistant.components.touchline_sl
|
||||
pytouchlinesl==0.1.7
|
||||
pytouchlinesl==0.1.8
|
||||
|
||||
# homeassistant.components.traccar
|
||||
# homeassistant.components.traccar_server
|
||||
@ -2477,7 +2477,7 @@ pywilight==0.0.74
|
||||
pywizlight==0.5.14
|
||||
|
||||
# homeassistant.components.wmspro
|
||||
pywmspro==0.2.0
|
||||
pywmspro==0.2.1
|
||||
|
||||
# homeassistant.components.ws66i
|
||||
pyws66i==1.1
|
||||
@ -2676,7 +2676,7 @@ soco==0.30.4
|
||||
solaredge-local==0.2.3
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.3.0
|
||||
solarlog_cli==0.3.1
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.1.1
|
||||
@ -2992,7 +2992,7 @@ xiaomi-ble==0.32.0
|
||||
xknx==3.2.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknxproject==3.8.0
|
||||
xknxproject==3.8.1
|
||||
|
||||
# homeassistant.components.fritz
|
||||
# homeassistant.components.rest
|
||||
|
@ -13,7 +13,7 @@ AIOSomecomfort==0.0.25
|
||||
Adax-local==0.1.5
|
||||
|
||||
# homeassistant.components.doorbird
|
||||
DoorBirdPy==3.0.2
|
||||
DoorBirdPy==3.0.4
|
||||
|
||||
# homeassistant.components.homekit
|
||||
HAP-python==4.9.1
|
||||
@ -42,7 +42,7 @@ PlexAPI==4.15.16
|
||||
ProgettiHWSW==0.1.3
|
||||
|
||||
# homeassistant.components.cast
|
||||
PyChromecast==14.0.1
|
||||
PyChromecast==14.0.3
|
||||
|
||||
# homeassistant.components.flick_electric
|
||||
PyFlick==0.0.2
|
||||
@ -164,7 +164,7 @@ aio-georss-gdacs==0.10
|
||||
aioairq==0.3.2
|
||||
|
||||
# homeassistant.components.airzone_cloud
|
||||
aioairzone-cloud==0.6.5
|
||||
aioairzone-cloud==0.6.6
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==0.9.3
|
||||
@ -186,7 +186,7 @@ aioaseko==1.0.0
|
||||
aioasuswrt==1.4.0
|
||||
|
||||
# homeassistant.components.husqvarna_automower
|
||||
aioautomower==2024.9.3
|
||||
aioautomower==2024.10.0
|
||||
|
||||
# homeassistant.components.azure_devops
|
||||
aioazuredevops==2.2.1
|
||||
@ -362,7 +362,7 @@ aiosolaredge==0.2.0
|
||||
aiosteamist==1.0.0
|
||||
|
||||
# homeassistant.components.cambridge_audio
|
||||
aiostreammagic==2.3.1
|
||||
aiostreammagic==2.5.0
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==4.0.3
|
||||
@ -401,7 +401,7 @@ aiowithings==3.0.3
|
||||
aioymaps==1.2.5
|
||||
|
||||
# homeassistant.components.airgradient
|
||||
airgradient==0.9.0
|
||||
airgradient==0.9.1
|
||||
|
||||
# homeassistant.components.airly
|
||||
airly==1.1.0
|
||||
@ -481,7 +481,7 @@ autarco==3.0.0
|
||||
axis==62
|
||||
|
||||
# homeassistant.components.fujitsu_fglair
|
||||
ayla-iot-unofficial==1.4.1
|
||||
ayla-iot-unofficial==1.4.2
|
||||
|
||||
# homeassistant.components.azure_event_hub
|
||||
azure-eventhub==5.11.1
|
||||
@ -786,7 +786,7 @@ freebox-api==1.1.0
|
||||
fritzconnection[qr]==1.13.2
|
||||
|
||||
# homeassistant.components.fyta
|
||||
fyta_cli==0.6.6
|
||||
fyta_cli==0.6.7
|
||||
|
||||
# homeassistant.components.google_translate
|
||||
gTTS==2.2.4
|
||||
@ -940,10 +940,10 @@ hole==0.8.0
|
||||
|
||||
# homeassistant.components.holiday
|
||||
# homeassistant.components.workday
|
||||
holidays==0.57
|
||||
holidays==0.58
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20241002.2
|
||||
home-assistant-frontend==20241002.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.10.2
|
||||
@ -987,7 +987,7 @@ idasen-ha==2.6.2
|
||||
ifaddr==0.2.0
|
||||
|
||||
# homeassistant.components.imgw_pib
|
||||
imgw_pib==1.0.5
|
||||
imgw_pib==1.0.6
|
||||
|
||||
# homeassistant.components.incomfort
|
||||
incomfort-client==0.6.3-1
|
||||
@ -1156,7 +1156,7 @@ mopeka-iot-ble==0.8.0
|
||||
motionblinds==0.6.25
|
||||
|
||||
# homeassistant.components.motionblinds_ble
|
||||
motionblindsble==0.1.1
|
||||
motionblindsble==0.1.2
|
||||
|
||||
# homeassistant.components.motioneye
|
||||
motioneye-client==0.3.14
|
||||
@ -1232,7 +1232,7 @@ numato-gpio==0.13.0
|
||||
numpy==1.26.4
|
||||
|
||||
# homeassistant.components.nyt_games
|
||||
nyt_games==0.4.2
|
||||
nyt_games==0.4.3
|
||||
|
||||
# homeassistant.components.google
|
||||
oauth2client==4.1.3
|
||||
@ -1274,7 +1274,7 @@ openhomedevice==2.2.0
|
||||
openwebifpy==4.2.7
|
||||
|
||||
# homeassistant.components.opower
|
||||
opower==0.8.0
|
||||
opower==0.8.3
|
||||
|
||||
# homeassistant.components.oralb
|
||||
oralb-ble==0.17.6
|
||||
@ -1396,7 +1396,7 @@ pyCEC==0.5.2
|
||||
pyControl4==1.2.0
|
||||
|
||||
# homeassistant.components.duotecno
|
||||
pyDuotecno==2024.9.0
|
||||
pyDuotecno==2024.10.0
|
||||
|
||||
# homeassistant.components.electrasmart
|
||||
pyElectra==1.2.4
|
||||
@ -1448,7 +1448,7 @@ pybalboa==1.0.2
|
||||
pyblackbird==0.6
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
pyblu==1.0.2
|
||||
pyblu==1.0.3
|
||||
|
||||
# homeassistant.components.neato
|
||||
pybotvac==0.0.25
|
||||
@ -1493,7 +1493,7 @@ pydroid-ipcam==2.0.0
|
||||
pyecoforest==0.4.0
|
||||
|
||||
# homeassistant.components.econet
|
||||
pyeconet==0.1.22
|
||||
pyeconet==0.1.23
|
||||
|
||||
# homeassistant.components.ista_ecotrend
|
||||
pyecotrend-ista==3.3.1
|
||||
@ -1798,7 +1798,7 @@ pysmartthings==0.7.8
|
||||
pysml==0.0.12
|
||||
|
||||
# homeassistant.components.smlight
|
||||
pysmlight==0.1.2
|
||||
pysmlight==0.1.3
|
||||
|
||||
# homeassistant.components.snmp
|
||||
pysnmp==6.2.6
|
||||
@ -1861,10 +1861,10 @@ python-izone==1.2.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.7.4
|
||||
python-kasa[speedups]==0.7.5
|
||||
|
||||
# homeassistant.components.linkplay
|
||||
python-linkplay==0.0.12
|
||||
python-linkplay==0.0.15
|
||||
|
||||
# homeassistant.components.matter
|
||||
python-matter-server==6.5.2
|
||||
@ -1919,7 +1919,7 @@ pytile==2023.12.0
|
||||
pytomorrowio==0.3.6
|
||||
|
||||
# homeassistant.components.touchline_sl
|
||||
pytouchlinesl==0.1.7
|
||||
pytouchlinesl==0.1.8
|
||||
|
||||
# homeassistant.components.traccar
|
||||
# homeassistant.components.traccar_server
|
||||
@ -1977,7 +1977,7 @@ pywilight==0.0.74
|
||||
pywizlight==0.5.14
|
||||
|
||||
# homeassistant.components.wmspro
|
||||
pywmspro==0.2.0
|
||||
pywmspro==0.2.1
|
||||
|
||||
# homeassistant.components.ws66i
|
||||
pyws66i==1.1
|
||||
@ -2122,7 +2122,7 @@ snapcast==2.3.6
|
||||
soco==0.30.4
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.3.0
|
||||
solarlog_cli==0.3.1
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.1.1
|
||||
@ -2381,7 +2381,7 @@ xiaomi-ble==0.32.0
|
||||
xknx==3.2.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknxproject==3.8.0
|
||||
xknxproject==3.8.1
|
||||
|
||||
# homeassistant.components.fritz
|
||||
# homeassistant.components.rest
|
||||
|
@ -165,6 +165,8 @@ EXCEPTIONS = {
|
||||
"tapsaff", # https://github.com/bazwilliams/python-taps-aff/pull/5
|
||||
"vincenty", # Public domain
|
||||
"zeversolar", # https://github.com/kvanzuijlen/zeversolar/pull/46
|
||||
# Using License-Expression (with hatchling)
|
||||
"ftfy", # Apache-2.0
|
||||
}
|
||||
|
||||
TODO = {
|
||||
|
@ -41,7 +41,7 @@ ZEROCONF_DISCOVERY_GEN3 = zeroconf.ZeroconfServiceInfo(
|
||||
ip_address="1.2.3.4",
|
||||
ip_addresses=[IPv4Address("1.2.3.4")],
|
||||
hostname="mock_hostname",
|
||||
name="Powerview Generation 3._powerview-g3._tcp.local.",
|
||||
name="Powerview Generation 3._PowerView-G3._tcp.local.",
|
||||
port=None,
|
||||
properties={},
|
||||
type="mock_type",
|
||||
|
@ -20,7 +20,7 @@
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Husqvarna',
|
||||
'model': 'HUSQVARNA AUTOMOWER® 450XH',
|
||||
'model': 'AUTOMOWER® 450XH',
|
||||
'model_id': None,
|
||||
'name': 'Test Mower 1',
|
||||
'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